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";| Export | Purpose |
|---|---|
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
| Framework | Export | Import from |
|---|---|---|
| Express | expressMiddleware() | errorcore |
| Fastify | fastifyPlugin | errorcore |
| Hono | honoMiddleware() | errorcore, errorcore/hono |
| Koa | koaMiddleware() | errorcore |
| Hapi | hapiPlugin | errorcore |
| Next.js | withErrorcore() | errorcore/nextjs |
Raw node:http | wrapHandler() | errorcore |
| AWS Lambda | wrapLambda() | 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:
| Mode | Intent |
|---|---|
fast | Lowest overhead. Stack and request metadata. |
safe | Conservative default capture. |
balanced | Locals and I/O with bounded budgets. |
forensic | Widest 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_DEKvia 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
schemaVersion1.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-Afterfrom 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.
| Field | Kind |
|---|---|
captureMode, adaptive | current state |
captured | counter |
dropped | counter |
droppedBreakdown.deduplicated | counter: duplicates suppressed by the fingerprint window |
droppedBreakdown.rateLimited | counter |
droppedBreakdown.captureFailed | counter |
droppedBreakdown.deadLetterWriteFailed | counter |
transportFailures | counter |
payloadSpool.pressureWarnings, .previewFallbacks, .drops | counters |
transportQueueDepth, deadLetterDepth, ioBufferDepth | gauges |
flushLatencyP50, flushLatencyP99 | percentiles over the last 512 completed sends |
lastFailureReason, lastFailureAt | most 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.