Skip to content

test(express): Error handler tests - #23724

Merged
isaacs merged 1 commit into
developfrom
sig/express-error-tests
Aug 28, 2026
Merged

test(express): Error handler tests#23724
isaacs merged 1 commit into
developfrom
sig/express-error-tests

Conversation

@s1gr1d

@s1gr1d s1gr1d commented Aug 28, 2026

Copy link
Copy Markdown
Member

The shouldHandleError  option is going to be removed from setupExpressErrorHandler. This PR just adds some tests to split up the PR a bit.

Two tests are marked it.fails. Both cover behaviour @isaacs raised on #23464.

  • The first shows the dedup marker is keyed on the request, not the error, so only the first error per request is captured. A 4xx that shouldHandleError skips still marks the request, so a later 500 is lost.

  • The second shows res.sentry is undefined whenever the integration captures first, because only the deprecated middleware sets it.

Reference:

@s1gr1d
s1gr1d requested a review from a team as a code owner August 28, 2026 09:59
@s1gr1d
s1gr1d requested review from a team, isaacs, msonnb, mydea and stephanie-anderson and removed request for a team August 28, 2026 09:59
Comment on lines +254 to +262
it('defers to the integration once the request is marked', () => {
const request = makeHandledRequest();
captureExceptionSpy.mockClear();
const error = Object.assign(new Error('boom'), { statusCode: 500 });

expressErrorHandler()(error, request, makeResponse(), vi.fn());

expect(captureExceptionSpy).not.toHaveBeenCalled();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: A request is marked as handled before checking if an error should be captured, causing subsequent, distinct errors on the same request to be silently dropped.
Severity: MEDIUM

Suggested Fix

Move the markExpressErrorHandled(request) call to after the shouldHandleError check. This ensures a request is only marked as handled if the error is actually captured and processed.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
packages/server-utils/test/integrations/express-error-handler.test.ts#L254-L262

Potential issue: The request is marked as handled by calling
`markExpressErrorHandled(request)` before the `shouldHandleError` check is performed. If
an initial error is skipped (e.g., a 400 error that is configured to be ignored), the
request is still marked. Consequently, if a second, distinct error occurs on the same
request (e.g., a 500 error), the `isExpressErrorHandled(request)` check will return
`true`, causing this subsequent error to be silently dropped instead of being captured.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +273 to +282
// TODO: `res.sentry` carries the captured event id, but only this middleware sets it.
// Once the integration captures first, apps reading `res.sentry` get undefined instead of the id.
it.fails('exposes the event id on the response when the integration captured the error', () => {
const res = makeResponse();
const error = Object.assign(new Error('boom'), { statusCode: 500 });

expressErrorHandler()(error, makeHandledRequest(), res, vi.fn());

expect(res.sentry).toBe('event-id');
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: The res.sentry property is not set on the response when an error is captured before the expressErrorHandler middleware runs, as the handler exits early.
Severity: LOW

Suggested Fix

Ensure that res.sentry is set on the response object whenever an error is captured by the Express integration, regardless of whether it's captured in the main error handler or another part of the instrumentation.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
packages/server-utils/test/integrations/express-error-handler.test.ts#L273-L282

Potential issue: When an error is captured by the integration before the main
`expressErrorHandler` middleware runs, the request is marked as handled. The
`expressErrorHandler` then sees this mark via `isExpressErrorHandled(request)` and exits
early by calling `next(error)`. As a result, the code that sets the Sentry event ID on
the response, `(res as { sentry?: string }).sentry = eventId;`, is never reached. This
leaves `res.sentry` as `undefined` for any downstream middleware that might rely on it.

Did we get this right? 👍 / 👎 to inform future reviews.

@mydea

mydea commented Aug 28, 2026

Copy link
Copy Markdown
Member

good stuff! isaacs review came in after I merged the PR, we may need/want to adjust some things based on it 😓

General thoughts:

  1. Keying errors by request+error: this makes sense, we should do this!
  2. res.sentry: this can/should go away IMHO, this is not really needed for users as default behavior imho.

@github-actions

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.56 kB - -
@sentry/browser - with treeshaking flags 26.92 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 26.82 kB - -
@sentry/browser (incl. Tracing) 48.63 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 48.64 kB - -
@sentry/browser (incl. Tracing, Profiling) 51.55 kB - -
@sentry/browser (incl. Tracing, Replay) 88.09 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.52 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 92.79 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 105.73 kB - -
@sentry/browser (incl. Feedback) 46.05 kB - -
@sentry/browser (incl. sendFeedback) 33.62 kB - -
@sentry/browser (incl. FeedbackAsync) 38.73 kB - -
@sentry/browser (incl. Metrics) 29.51 kB - -
@sentry/browser (incl. Logs) 29.8 kB - -
@sentry/browser (incl. Metrics & Logs) 30.43 kB - -
@sentry/react 30.3 kB - -
@sentry/react (incl. Tracing) 50.83 kB - -
@sentry/vue 35.69 kB - -
@sentry/vue (incl. Tracing) 50.86 kB - -
@sentry/svelte 28.59 kB - -
CDN Bundle 30.35 kB - -
CDN Bundle (incl. Tracing) 49.23 kB - -
CDN Bundle (incl. Logs, Metrics) 32.58 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 51.12 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.17 kB - -
CDN Bundle (incl. Tracing, Replay) 86.72 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.6 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 92.62 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.5 kB - -
CDN Bundle - uncompressed 89.95 kB - -
CDN Bundle (incl. Tracing) - uncompressed 146.82 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 96.24 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 152.51 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 225.41 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 266.32 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 272 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 280.02 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 285.69 kB - -
@sentry/nextjs (client) 53.41 kB - -
@sentry/sveltekit (client) 49.07 kB - -
@sentry/core/server 65.15 kB - -
@sentry/core/browser 51.8 kB - -
@sentry/node 122.74 kB +0.02% +16 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 85.23 kB - -
@sentry/node - without tracing 87.74 kB +0.03% +25 B 🔺
@sentry/node - without channel injection 102.4 kB +0.02% +16 B 🔺
@sentry/aws-serverless 95.9 kB +0.03% +23 B 🔺
@sentry/cloudflare (withSentry) - minified 199.69 kB - -
@sentry/cloudflare (withSentry) 496.19 kB - -

View base workflow run

@isaacs isaacs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome work, I was going to create an issue to circle back on this, but you beat me to it ;)

It's hilarious that the bots are upset about the tests showing failures, when that's exactly the point. 🤖🤪

Landing this, and then we can make them pass in a follow-up.

@isaacs
isaacs merged commit 189fc86 into develop Aug 28, 2026
208 checks passed
@isaacs
isaacs deleted the sig/express-error-tests branch August 28, 2026 15:27
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.

3 participants