Skip to content

feat(node): Capture Express errors automatically via expressIntegration - #23464

Draft
mydea wants to merge 2 commits into
developfrom
feat/express-error-capture
Draft

feat(node): Capture Express errors automatically via expressIntegration#23464
mydea wants to merge 2 commits into
developfrom
feat/express-error-capture

Conversation

@mydea

@mydea mydea commented Aug 20, 2026

Copy link
Copy Markdown
Member

expressIntegration() now captures errors thrown from Express route handlers automatically, so setupExpressErrorHandler(app) is no longer needed. Capture happens on the routing layer's handle_request diagnostics channel — i.e. at the throw site, before any user error-handling middleware runs — and is gated by a new shouldHandleError option on the integration.

shouldHandleError

  • Omitted → default gate: 5xx errors (and errors without a resolvable status) are captured; 3xx/4xx are not.
  • A function (error) => boolean → custom gate.
  • false → disables Sentry's automatic capture entirely. Use this as an escape hatch when you want to capture errors yourself via Sentry.captureException from your own middleware.

The option is passed into the instrumentation directly (not read back from the integration instance), matching the approach used for Fastify/Hapi.

Why this shape

  • Capturing on the channel (rather than the middleware) means we no longer depend on middleware ordering, and errors are captured before a user handler can swallow them. Duplicate captures — the error bubbling through parent layers, or a user still calling setupExpressErrorHandler — are collapsed by captureException's per-object __sentry_captured__ dedup, so only one event is sent.
  • next('route') / next('router') control-flow signals are filtered out so they aren't mistaken for errors.
  • Because capture happens at the throw site, shouldHandleError sees the error as thrown (its own status), not a final response status — this is intentional and matches the other framework integrations.

Deprecations

The core Express exports are now superseded by expressIntegration() and are unused internally. They are deprecated and will be removed in the next major: setupExpressErrorHandler, expressErrorHandler, patchExpressModule, and the types ExpressIntegrationOptions, ExpressHandlerOptions, ExpressMiddleware, ExpressErrorMiddleware. Downstream re-exports keep working (lint suppressed at the re-export sites) and surface the deprecation via TypeScript.

Behavior change

Express errors are now captured without setupExpressErrorHandler, and the resulting mechanism type is auto.http.express (the channel wins over the deprecated middleware, which is deduped). Existing e2e/integration assertions were updated accordingly. shouldHandleError passed to the deprecated setupExpressErrorHandler no longer takes effect — configure it on expressIntegration() instead.

A migration guide entry is included.

…ion`

`expressIntegration()` now captures errors thrown from route handlers on its own,
at the throw site (before user error-handling middleware runs), gated by a new
`shouldHandleError` option. Passing `shouldHandleError: false` opts out entirely.

This moves Express error capture into `@sentry/server-utils` (alongside the
channel-based tracing) and deprecates the now-superseded core Express exports
(`setupExpressErrorHandler`, `expressErrorHandler`, `patchExpressModule` and the
related types), to be removed in the next major.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0e8547a. Configure here.

Comment thread packages/server-utils/src/integrations/express/instrumentation.ts
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 30.3 kB - -
@sentry/browser - with treeshaking flags 28.47 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 26.81 kB - -
@sentry/browser (incl. Tracing) 48.58 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 48.59 kB - -
@sentry/browser (incl. Tracing, Profiling) 51.46 kB - -
@sentry/browser (incl. Tracing, Replay) 87.98 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.36 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 92.7 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 105.39 kB - -
@sentry/browser (incl. Feedback) 47.65 kB - -
@sentry/browser (incl. sendFeedback) 35.13 kB - -
@sentry/browser (incl. FeedbackAsync) 40.28 kB - -
@sentry/browser (incl. Metrics) 31.24 kB - -
@sentry/browser (incl. Logs) 31.52 kB - -
@sentry/browser (incl. Metrics & Logs) 32.15 kB - -
@sentry/react 32.09 kB - -
@sentry/react (incl. Tracing) 50.77 kB - -
@sentry/vue 35.34 kB - -
@sentry/vue (incl. Tracing) 50.54 kB - -
@sentry/svelte 30.33 kB - -
CDN Bundle 31.61 kB - -
CDN Bundle (incl. Tracing) 48.9 kB - -
CDN Bundle (incl. Logs, Metrics) 33.8 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 50.82 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 74.31 kB - -
CDN Bundle (incl. Tracing, Replay) 86.48 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.31 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 92.19 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.12 kB - -
CDN Bundle - uncompressed 93.84 kB - -
CDN Bundle (incl. Tracing) - uncompressed 146.75 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 100.14 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 152.44 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 229.08 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 266.01 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 271.68 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 279.71 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 285.37 kB - -
@sentry/nextjs (client) 53.29 kB - -
@sentry/sveltekit (client) 49 kB - -
@sentry/core/server 65.38 kB - -
@sentry/core/browser 51.72 kB - -
@sentry/node 117.53 kB +0.14% +158 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 0 B added added
@sentry/node - without tracing 81.91 kB +0.03% +24 B 🔺
@sentry/aws-serverless 91.32 kB +0.03% +24 B 🔺
@sentry/cloudflare (withSentry) - minified 213.98 kB - -
@sentry/cloudflare (withSentry) 528.93 kB - -

View base workflow run

The channel `error` event runs outside the layer span's async context, so
`captureException` was recording events with no `parent_span_id`. Re-activate the
span bound by `bindTracingChannelToSpan` (now typed on `HandleChannelContext`)
around the capture so the error event is parented to the request's trace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant