diff --git a/MIGRATION.md b/MIGRATION.md index d892c301db0d..c8726d67c16e 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -863,6 +863,29 @@ If you prefer to capture errors yourself, set `expressIntegration({ shouldHandle The `expressErrorHandler` and `patchExpressModule` exports are deprecated for the same reason and will be removed in the next major version. The export of `expressErrorHandler` and `setupExpressErrorHandler` is moved from `@sentry/core` to `@sentry/server-utils`. +The `setupExpressErrorHandler` and `expressErrorHandler` no longer accept a `shouldHandleError` option, and the `ExpressHandlerOptions` type was removed. Set the callback on `expressIntegration()` instead: + +```diff + Sentry.init({ +- integrations: [Sentry.expressIntegration()], ++ integrations: [ ++ Sentry.expressIntegration({ ++ shouldHandleError(error) { ++ return Number(error.statusCode ?? 500) >= 400; ++ }, ++ }), ++ ], + }); + +-Sentry.setupExpressErrorHandler(app, { +- shouldHandleError(error) { +- return (error.statusCode ?? 500) >= 400; +- }, +-}); +``` + +`setupExpressErrorHandler(app)` keeps working for the time being. It captures 5xx errors and errors without a status, and cannot be filtered in the error handler. + ### `onUnhandledRejectionIntegration`: no warning before `Error` rejections in `strict` mode Affected SDKs: `@sentry/node` and all dependents. @@ -1297,6 +1320,7 @@ The `idleTimeout`, `finalTimeout` and `childSpanTimeout` options of interaction - (AWS Lambda) The deprecated `startTrace` option was removed. It no longer had any effect; to disable tracing, set `tracesSampleRate` to `0`. - (AWS Lambda) The deprecated `tryPatchHandler` function was removed. It was no longer used. - (Express) The deprecated `patchExpressModule(options)` signature was removed. Use `patchExpressModule(moduleExports, getOptions)` instead. +- (Express) The `shouldHandleError` option was removed from `setupExpressErrorHandler` and `expressErrorHandler`, along with the `ExpressHandlerOptions` type. Configure it on `expressIntegration()` instead. See [Express: errors are captured automatically](#express-errors-are-captured-automatically). - (Fastify) The deprecated `instrumentFastify` and `handleFastifyError` exports were removed. `fastifyIntegration` now instruments Fastify (v3.21–v5) and captures errors on its own, so neither export is needed. See [Fastify: `setupFastifyErrorHandler` is deprecated](#fastify-setupfastifyerrorhandler-is-deprecated). - The `@sentry/node-core/light/otlp` entry point was removed, along with its optional `@opentelemetry/exporter-trace-otlp-http` peer dependency. `otlpIntegration` is now exported directly from every server-side SDK, so `Sentry.otlpIntegration()` needs no extra import or install. - The `otlpIntegration` options `setupOtlpTracesExporter` and `collectorUrl` were removed, and the integration no longer sets up a span exporter, span processor, or tracer provider. Configure your own exporter and point it at `Sentry.getOtlpTracesEndpoint(dsn)`, or at your collector's URL if you route through one. See [Connecting Sentry to your OpenTelemetry traces](#connecting-sentry-to-your-opentelemetry-traces). diff --git a/dev-packages/node-integration-tests/suites/express/handle-error/scenario-setup-error-handler-fallback.mjs b/dev-packages/node-integration-tests/suites/express/handle-error/scenario-setup-error-handler-fallback.mjs index e0ea1168ee0f..5608632c4958 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error/scenario-setup-error-handler-fallback.mjs +++ b/dev-packages/node-integration-tests/suites/express/handle-error/scenario-setup-error-handler-fallback.mjs @@ -8,7 +8,10 @@ const app = express(); app.use(cors()); app.get('/test1', (_req, _res) => { - throw new Error('error_1'); + // 4xx errors are skipped by the default predicate + const error = new Error('error_1'); + error.statusCode = 404; + throw error; }); app.get('/test2', (_req, _res) => { @@ -16,9 +19,8 @@ app.get('/test2', (_req, _res) => { }); // With `expressIntegration` disabled (see the instrument file), the deprecated middleware is the sole -// capturer and its own `shouldHandleError` applies. -Sentry.setupExpressErrorHandler(app, { - shouldHandleError: error => error.message === 'error_2', -}); +// capturer. It has no `shouldHandleError` of its own, so the default predicate applies: 5xx and +// status-less errors are captured, 3xx/4xx are not. +Sentry.setupExpressErrorHandler(app); startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/express/handle-error/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error/test.ts index 3e6f8ce1feac..78ee20e6a3bc 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error/test.ts @@ -336,14 +336,15 @@ describe('express error handling', () => { }, ); - // Fallback: with `expressIntegration` disabled, the deprecated middleware is the sole capturer and - // its own `shouldHandleError` applies (mechanism `auto.middleware.express`). + // Fallback: with `expressIntegration` disabled, the deprecated middleware is the sole capturer + // (mechanism `auto.middleware.express`). It applies the default predicate — `shouldHandleError` + // is configured on `expressIntegration` only. createCjsTests( __dirname, 'scenario-setup-error-handler-fallback.mjs', 'instrument-setup-error-handler.mjs', (createRunner, test) => { - test('deprecated handler captures with its own shouldHandleError when expressIntegration is disabled', async () => { + test('deprecated handler captures with the default predicate when expressIntegration is disabled', async () => { const runner = createRunner() .expect({ event: { @@ -362,9 +363,9 @@ describe('express error handling', () => { }) .start(); - // this error is filtered & ignored + // 4xx: skipped by the default predicate runner.makeRequest('get', '/test1', { expectError: true }); - // this error is actually captured + // no status: treated as 5xx and captured runner.makeRequest('get', '/test2', { expectError: true }); await runner.completed(); diff --git a/packages/core/src/integrations/express/types.ts b/packages/core/src/integrations/express/types.ts index 3affb2d51284..5bf33e578ad0 100644 --- a/packages/core/src/integrations/express/types.ts +++ b/packages/core/src/integrations/express/types.ts @@ -187,15 +187,3 @@ export type ExpressErrorMiddleware = ( res: ExpressResponse, next: (error: MiddlewareError) => void, ) => void; - -/** - * @deprecated `expressIntegration()` captures errors automatically; pass `shouldHandleError` to it to - * customize capture. This type is deprecated and will be removed in the next major version. - */ -export interface ExpressHandlerOptions { - /** - * Callback method deciding whether error should be captured and sent to Sentry - * @param error Captured middleware error - */ - shouldHandleError?(this: void, error: MiddlewareError): boolean; -} diff --git a/packages/core/src/server-exports.ts b/packages/core/src/server-exports.ts index ba175bfe58a7..7df98fc3a756 100644 --- a/packages/core/src/server-exports.ts +++ b/packages/core/src/server-exports.ts @@ -21,7 +21,6 @@ export { safeUnref as _INTERNAL_safeUnref } from './utils/timer'; export { patchExpressModule } from './integrations/express/index'; export type { ExpressIntegrationOptions, - ExpressHandlerOptions, ExpressMiddleware, ExpressErrorMiddleware, } from './integrations/express/types'; diff --git a/packages/server-utils/src/index.ts b/packages/server-utils/src/index.ts index 88af16be373e..23ca1098ef84 100644 --- a/packages/server-utils/src/index.ts +++ b/packages/server-utils/src/index.ts @@ -53,7 +53,7 @@ export { vercelAIIntegration } from './integrations/vercel-ai'; export { expressIntegration } from './integrations/express'; /* oxlint-disable typescript/no-deprecated -- deprecated Express error-handler exports, kept until the next major */ export { expressErrorHandler, setupExpressErrorHandler } from './integrations/express/error-handler'; -export type { ExpressHandlerOptions } from './integrations/express/types'; +export type { ExpressIntegrationOptions } from './integrations/express/types'; /* oxlint-enable typescript/no-deprecated */ export { firebaseIntegration } from './integrations/firebase'; diff --git a/packages/server-utils/src/integrations/express/error-handler.ts b/packages/server-utils/src/integrations/express/error-handler.ts index b081bd207402..804b923f798d 100644 --- a/packages/server-utils/src/integrations/express/error-handler.ts +++ b/packages/server-utils/src/integrations/express/error-handler.ts @@ -1,6 +1,6 @@ import { captureException, getIsolationScope, httpRequestToRequestData } from '@sentry/core'; import { isExpressErrorHandled } from './error-handled'; -import type { ExpressHandlerOptions, ExpressRequest, ExpressResponse, MiddlewareError } from './types'; +import type { ExpressRequest, ExpressResponse, MiddlewareError } from './types'; import { defaultShouldHandleError } from './utils'; type ExpressErrorMiddleware = ( @@ -28,9 +28,9 @@ function setSDKProcessingMetadata(request: ExpressRequest): void { * An Express-compatible error handler, used by {@link setupExpressErrorHandler}. * * @deprecated `expressIntegration()` now captures errors automatically. This export is deprecated and - * will be removed in the next major version. + * will be removed in the next major version. Migrate to the `expressIntegration` to filter with `shouldHandleError`. */ -export function expressErrorHandler(options?: ExpressHandlerOptions): ExpressErrorMiddleware { +export function expressErrorHandler(): ExpressErrorMiddleware { return function sentryErrorMiddleware(error, request, res, next): void { // When an error happens, the request handler middleware does not run, so we set it here too. setSDKProcessingMetadata(request); @@ -45,9 +45,9 @@ export function expressErrorHandler(options?: ExpressHandlerOptions): ExpressErr return; } - const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError; - - if (shouldHandleError(error)) { + // `shouldHandleError` is an `expressIntegration()` feature and is deliberately not honoured here: + // this path exists to keep capturing errors, not to filter them. + if (defaultShouldHandleError(error)) { const eventId = captureException(error, { mechanism: { type: 'auto.middleware.express', handled: false }, }); @@ -71,20 +71,16 @@ function expressRequestHandler(): ExpressMiddleware { * The error handler must be before any other middleware and after all controllers. * * @param app The Express instance - * @param options {ExpressHandlerOptions} Configuration options for the handler * - * @deprecated `expressIntegration()` now captures errors automatically, so calling this is no longer + * @deprecated `expressIntegration()` now captures errors automatically, so calling this error handler is no longer * necessary. To customize which errors are captured, pass `shouldHandleError` to `expressIntegration()`. * This export is deprecated and will be removed in the next major version. */ -export function setupExpressErrorHandler( - app: { - // oxlint-disable-next-line no-explicit-any - use: (middleware: any) => unknown; - }, - options?: ExpressHandlerOptions, -): void { +export function setupExpressErrorHandler(app: { + // oxlint-disable-next-line no-explicit-any + use: (middleware: any) => unknown; +}): void { app.use(expressRequestHandler()); // oxlint-disable-next-line typescript/no-deprecated - app.use(expressErrorHandler(options)); + app.use(expressErrorHandler()); } diff --git a/packages/server-utils/src/integrations/express/types.ts b/packages/server-utils/src/integrations/express/types.ts index c5c038ba243f..5536f524e849 100644 --- a/packages/server-utils/src/integrations/express/types.ts +++ b/packages/server-utils/src/integrations/express/types.ts @@ -73,12 +73,6 @@ export interface MiddlewareError extends Error { /** Callback deciding whether an error should be captured; `false` disables capture entirely. */ export type ExpressShouldHandleError = ((error: MiddlewareError) => boolean) | false; -/** Options for the deprecated `setupExpressErrorHandler` / `expressErrorHandler`. */ -export interface ExpressHandlerOptions { - /** Callback deciding whether an error should be captured and sent to Sentry. */ - shouldHandleError?: (error: MiddlewareError) => boolean; -} - type IgnoreMatcher = string | RegExp | ((name: string) => boolean); export interface ExpressIntegrationOptions { /** Ignore specific based on their name */