Error monitoring with execution context for Node.js.
Errorcore captures the state around a failure so you can inspect what happened at the point an error was thrown. It focuses on request-level context, execution flow, and minimal setup.
- Optionally captures local variables and arguments at the point an error is thrown
- Tracks request context across async boundaries using AsyncLocalStorage
- Records outbound IO in sequence
- Attaches process and environment metadata with optional scrubbing
- Encrypts payloads before transport
- Buffers failed deliveries and retries when the network is available
- Propagates W3C
traceparent/tracestateheaders for cross-service error context
npm install errorcoreAdd two lines to the top of your application entry point:
const errorcore = require('errorcore');
errorcore.init();That's it. In development (NODE_ENV !== 'production'), errorcore defaults to stdout transport and unencrypted payloads. Throw an error and the captured payload prints to your terminal.
npm install errorcore
npx errorcore init --quickstart
node errorcore-test.js// Express
const { expressMiddleware } = require('errorcore');
app.use(expressMiddleware());
// Fastify
const { fastifyPlugin } = require('errorcore');
fastify.register(fastifyPlugin);
// Koa
const { koaMiddleware } = require('errorcore');
app.use(koaMiddleware());Before deploying, configure a transport and encryption key:
// errorcore.config.js
module.exports = {
transport: {
type: 'http',
url: 'https://collector.example.com/v1/errors',
authorization: 'Bearer <token>',
protocol: 'auto',
},
encryptionKey: process.env.ERRORCORE_DEK,
macKey: process.env.ERRORCORE_MAC_KEY,
};Generate a key: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
HTTP collector transport defaults to protocol: 'auto': HTTPS collectors try HTTP/2 first and fall back to HTTP/1.1 only when negotiation fails; plain HTTP uses HTTP/1.1 only and still requires allowPlainHttpTransport: true. Set protocol: 'http1' to force HTTP/1.1 or protocol: 'http2' to require HTTPS HTTP/2. The SDK does not instrument application HTTP/2 servers or clients in this release.
Manual propagation helpers are available when framework middleware is not enough:
const errorcore = require('errorcore');
errorcore.withTraceContext({ traceparent, tracestate, method: 'POST', url: '/job' }, () => {
const headers = errorcore.getTraceHeaders();
// attach headers.traceparent / headers.tracestate to outbound work
});This is W3C propagation and package metadata only. There is no span API, exporter, OpenTelemetry bridge, tracing UI, ingestion layer, reconstruction layer, default-on locals, or identity extraction in this SDK release.
Errorcore records DB query events differently depending on your runtime environment.
Tier 1: Plain Node.js (Express, Fastify, Koa, NestJS, raw http): automatic. No config needed. All recorders install against the same require() graph the app uses.
Tier 2: Single-graph bundlers (Vite SSR, esbuild, plain webpack): automatic if the driver is not tree-shaken, or pass explicit references:
errorcore.init({
drivers: { pg: require('pg'), mongodb: require('mongodb') },
});Tier 3: Next.js App Router: externalize drivers from the webpack bundle:
// next.config.js
module.exports = {
serverExternalPackages: ['pg', 'mongodb', 'mysql2', 'ioredis'],
};Without this, the DB timeline will not populate. The startup diagnostic will report warn(bundled-unpatched). HTTP inbound, HTTP outbound, and fetch (undici) recording work in all three tiers.
At startup, errorcore prints one line listing the state of each recorder:
[errorcore] 0.2.0 node=20.11.0 recorders: http-server=ok http-client=ok undici=ok net=ok dns=ok pg=skip(not-installed) mongodb=warn(bundled-unpatched) mysql2=skip(not-installed) ioredis=skip(not-installed)
Three states: ok (active), skip(<reason>) (intentionally inactive, no action needed), warn(<reason>) (wanted to install but couldn't, action required). When warns are present, the output grows to 3–6 lines with one actionable guidance line per warn state. Suppress the entire block with config.silent: true.
To capture errors and optionally non-2xx responses from Clerk-style middleware rejections, wrap your middleware with withNextMiddleware:
import { withNextMiddleware } from 'errorcore/nextjs';
import { clerkMiddleware } from '@clerk/nextjs/server';
export default withNextMiddleware(clerkMiddleware());Control which response status codes trigger a capture:
errorcore.init({
captureMiddlewareStatusCodes: [500, 502, 503, 504], // or 'all', or 'none' (default)
});undefined returns (pass-through middleware) are never captured regardless of this setting. The ALS context started by withNextMiddleware propagates automatically into the downstream route handler.
npm test # unit + always-on integration
npm run coverage # produce a coverage/ report
EC_INTEGRATION_PG=1 npm test # add real-pg suite (needs Postgres)
EC_INTEGRATION_MYSQL=1 npm test # add real-mysql suite (needs MySQL)
EC_SMOKE_NEXTJS=1 npm test # run the Next.js smoke harness tooThe mongodb integration suite uses mongodb-memory-server and runs
unconditionally — the binary is downloaded once and cached at
~/.cache/mongodb-binaries. The ioredis suite runs against an
in-process RESP-2 stub so it always runs too.
The pg and mysql2 suites are gated behind environment variables
because they need a reachable database. CI does not run them; see
CONTRIBUTING.md for
the local docker / podman one-liners.
Coverage baseline (recorded 2026-05-01): 73.9% statements, 64.52% branches, 80.94% functions, 74.95% lines. The threshold is intentionally not enforced; the report is observability-only. Run npm run coverage and open coverage/index.html for the per-file breakdown.
Report vulnerabilities via issues or privately.
Encryption key rotation is supported via previousEncryptionKeys in the config. See the Key rotation runbook for the operational sequence.
PolyForm Small Business 1.0.0. Free for individuals and companies under $1M revenue. Commercial license required above that threshold.
