diff --git a/MIGRATION.md b/MIGRATION.md index a4a9599ee0e9..216af4d5c6ef 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -862,6 +862,14 @@ 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`. +### `onUnhandledRejectionIntegration`: no warning before `Error` rejections in `strict` mode + +Affected SDKs: `@sentry/node` and all dependents. + +In `strict` mode, `onUnhandledRejectionIntegration` printed the warning `This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:` in front of every unhandled rejection. It is now printed only when the rejection reason has no stack trace, which matches Node.js. A rejection with an `Error` reason prints the error alone. + +The process still exits with code `1` and the reason is still written to `stderr` in both cases. If you match on that warning text in log processing or in tests, update it. + ### Span name changes Affected SDKs: All SDKs. diff --git a/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/mode-strict-error.js b/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/mode-strict-error.js new file mode 100644 index 000000000000..7204bb8482a1 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/mode-strict-error.js @@ -0,0 +1,11 @@ +const Sentry = require('@sentry/node'); +const { expectProcessToExit } = require('../../../utils/expect-process-to-exit'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.onUnhandledRejectionIntegration({ mode: 'strict' })], +}); + +expectProcessToExit(); + +Promise.reject(new Error('test rejection')); diff --git a/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts b/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts index c8570747cf8d..2b90612ed505 100644 --- a/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/onUnhandledRejectionIntegration/test.ts @@ -59,6 +59,22 @@ test rejection`); }); })); + test('should not show warning for error-type promise rejections in strict mode', () => + new Promise(done => { + expect.assertions(5); + + const testScriptPath = path.resolve(__dirname, 'mode-strict-error.js'); + + childProcess.execFile('node', [testScriptPath], { encoding: 'utf8' }, (err, stdout, stderr) => { + expect(err).not.toBeNull(); + expect(err?.code).toBe(1); + expect(stdout).not.toBe("I'm alive!"); + expect(stderr).toContain('Error: test rejection'); + expect(stderr).not.toContain('The promise rejected with the reason'); + done(); + }); + })); + test('should not close process or warn on unhandled rejection in none mode', () => new Promise(done => { expect.assertions(3); diff --git a/packages/node/src/integrations/onunhandledrejection.ts b/packages/node/src/integrations/onunhandledrejection.ts index 16a7b7a402d3..e8abddde34e0 100644 --- a/packages/node/src/integrations/onunhandledrejection.ts +++ b/packages/node/src/integrations/onunhandledrejection.ts @@ -145,9 +145,11 @@ function handleRejection(reason: unknown, mode: UnhandledRejectionMode): void { console.error(isObjectLike(reason) && 'stack' in reason ? reason.stack : reason); }); } else if (mode === 'strict') { - consoleSandbox(() => { - console.warn(rejectionWarning); - }); + if (!(isObjectLike(reason) && 'stack' in reason)) { + consoleSandbox(() => { + console.warn(rejectionWarning); + }); + } logAndExitProcess(reason); } /* eslint-enable no-console */