errorcoredocsbeta

SDK reference

The errorcore package is a Node.js SDK. It captures runtime evidence around a thrown error, scrubs it, encrypts it in-process, and posts it to the ingest API.

Public API

import { init, captureError, flush, shutdown, getHealth } from "errorcore";
ExportPurpose
init(config)Creates and activates the SDK instance. Returns the instance.
captureError(error, options?)Captures an error explicitly, outside a framework adapter.
flush()Awaits delivery of everything currently buffered.
shutdown()Flushes, then tears the instance down.
getHealth()Returns a HealthSnapshot, or null before init().

init() stores the instance in a global symbol registry so it survives webpack chunk boundaries; the same pattern OpenTelemetry and Prisma use. Calling init() while an instance is already active logs a warning and returns the existing instance rather than creating a second one.

Initialization

init({
  service: "core-api",
  deploymentEnv: process.env.ERRORCORE_ENVIRONMENT,
  transport: {
    type: "http",
    url: process.env.ERRORCORE_INGEST_URL ?? "https://api-production-7ecf.up.railway.app/v1/ingest",
    apiKey: process.env.ERRORCORE_API_KEY,
  },
  encryptionKey: process.env.ERRORCORE_DEK,
});

transport must be configured explicitly in production. stdout and file transports exist for local work. transport.apiKey and transport.authorization are mutually exclusive; supplying both throws.

Resolution order for the service name: service, then OTEL_SERVICE_NAME, then npm_package_name, then unknown-service.

Framework adapters

FrameworkExportImport from
ExpressexpressMiddleware()errorcore
FastifyfastifyPluginerrorcore
HonohonoMiddleware()errorcore, errorcore/hono
KoakoaMiddleware()errorcore
HapihapiPluginerrorcore
Next.jswithErrorcore()errorcore/nextjs
Raw node:httpwrapHandler()errorcore
AWS LambdawrapLambda()errorcore

There is no NestJS adapter. See the NestJS guide for the honest integration path.

The Next.js edge runtime resolves to a no-op stub, so edge bundles never pull in the inspector, filesystem, or transport code.

Capture modes

captureMode selects how much runtime state is collected:

ModeIntent
fastLowest overhead. Stack and request metadata.
safeConservative default capture.
balancedLocals and I/O with bounded budgets.
forensicWidest capture, highest overhead.

Modes can be switched at runtime; mode-relevant user overrides are reapplied to each new mode state. Local-variable capture is additionally guarded by a pause budget (localsGuard) so inspector pauses cannot dominate the event loop.

Deduplication

Identical captures are suppressed by a fingerprint window of roughly 10 seconds. A suppressed capture is counted in droppedBreakdown.deduplicated; it is not sent and it does not consume plan allowance.

Wire contract

The HTTP transport posts Content-Type: application/errorcore+json with envelope version 2. The full body shape is in the API reference.

The SDK:

  • encrypts the payload with AES-256-GCM using a key derived from ERRORCORE_DEK via HKDF-SHA256,
  • signs the envelope with an outer HMAC-SHA256 that the receiver verifies before decrypting,
  • raw-deflate compresses plaintext larger than 8 KiB before encryption,
  • emits inner schemaVersion 1.3.0, and
  • sends the API key as Authorization: Bearer ec_live_….

The inner and outer eventId must match; the receiver quarantines any package where they differ.

Retries

The HTTP transport makes at most 3 attempts per package.

  • Backoff delays: 200 ms then 600 ms, each with jitter.
  • Total retry budget: 30 seconds. Once elapsed, no further attempt is made.
  • Retry-After from the server overrides the computed delay.

Retryable statuses: 408, 429, 500, 502, 503, 504. Transient network errors (connection reset, timeout, DNS failure and similar) are retryable too.

Everything else is terminal, including 403 plan-limit and envelope-authentication rejections; retrying those cannot succeed.

Packages that exhaust their retries go to the dead-letter spool when deadLetterPath is configured.

Health metrics

getHealth() returns a HealthSnapshot. Counters are monotonic since init(); gauges are sampled at call time.

FieldKind
captureMode, adaptivecurrent state
capturedcounter
droppedcounter
droppedBreakdown.deduplicatedcounter: duplicates suppressed by the fingerprint window
droppedBreakdown.rateLimitedcounter
droppedBreakdown.captureFailedcounter
droppedBreakdown.deadLetterWriteFailedcounter
transportFailurescounter
payloadSpool.pressureWarnings, .previewFallbacks, .dropscounters
transportQueueDepth, deadLetterDepth, ioBufferDepthgauges
flushLatencyP50, flushLatencyP99percentiles over the last 512 completed sends
lastFailureReason, lastFailureAtmost recent transport rejection, or null

Invariant: dropped equals the sum of its breakdown fields.

These metrics stay in your process. They are not sent to errorcore.

Types

SDKConfig, ResolvedConfig, ErrorPackage, Completeness, TraceContextInput, TraceHeaders, HealthSnapshot, LambdaContext.

ResolvedConfig is a public surface and deliberately excludes secrets: the DEK, MAC key, previous keys, and transport authorization are held on a runtime-only object and never exposed through instance.config.

On this page