diff --git a/doc/api/cli.md b/doc/api/cli.md index ba7cd510f31c9e..1678083d4d584d 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -978,20 +978,17 @@ added: - v10.17.0 --> -By default all unhandled rejections trigger a warning plus a deprecation warning -for the very first unhandled rejection in case no [`unhandledRejection`][] hook -is used. - Using this flag allows to change what should happen when an unhandled rejection occurs. One of the following modes can be chosen: +* `warn-with-error-code`: Emit [`unhandledRejection`][]. If this hook is not + set, trigger a warning, and set the process exit code to 66. This is the + default. * `throw`: Emit [`unhandledRejection`][]. If this hook is not set, raise the unhandled rejection as an uncaught exception. * `strict`: Raise the unhandled rejection as an uncaught exception. * `warn`: Always trigger a warning, no matter if the [`unhandledRejection`][] hook is set or not but do not print the deprecation warning. -* `warn-with-error-code`: Emit [`unhandledRejection`][]. If this hook is not - set, trigger a warning, and set the process exit code to 1. * `none`: Silence all warnings. ### `--use-bundled-ca`, `--use-openssl-ca` diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index f2145d425c620f..a96c9efc3160d3 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -53,14 +53,11 @@ const kThrowUnhandledRejections = 3; // --unhandled-rejections=warn-with-error-code: // Emit 'unhandledRejection', if it's unhandled, emit -// 'UnhandledPromiseRejectionWarning', then set process exit code to 1. +// 'UnhandledPromiseRejectionWarning', then set process exit code to 66. const kWarnWithErrorCodeUnhandledRejections = 4; -// --unhandled-rejections is unset: -// Emit 'unhandledRejection', if it's unhandled, emit -// 'UnhandledPromiseRejectionWarning', then emit deprecation warning. -const kDefaultUnhandledRejections = 5; +const unhandledRejectionExitCode = 66; let unhandledRejectionsMode; @@ -86,7 +83,7 @@ function getUnhandledRejectionsMode() { case 'warn-with-error-code': return kWarnWithErrorCodeUnhandledRejections; default: - return kDefaultUnhandledRejections; + return kWarnWithErrorCodeUnhandledRejections; } } @@ -151,12 +148,14 @@ function handledRejection(promise) { } const unhandledRejectionErrName = 'UnhandledPromiseRejectionWarning'; -function emitUnhandledRejectionWarning(uid, reason) { +function emitUnhandledRejectionWarning(uid, reason, failing) { const warning = getErrorWithoutStack( unhandledRejectionErrName, 'Unhandled promise rejection. 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(). ' + + (failing ? 'This process will terminate with exit code ' + + unhandledRejectionExitCode + '. ' : '') + 'To terminate the node process on unhandled promise ' + 'rejection, use the CLI flag `--unhandled-rejections=strict` (see ' + 'https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). ' + @@ -175,15 +174,6 @@ function emitUnhandledRejectionWarning(uid, reason) { process.emitWarning(warning); } -let deprecationWarned = false; -function emitDeprecationWarning() { - process.emitWarning( - 'Unhandled promise rejections are deprecated. In the future, ' + - 'promise rejections that are not handled will terminate the ' + - 'Node.js process with a non-zero exit code.', - 'DeprecationWarning', 'DEP0018'); -} - // If this method returns true, we've executed user code or triggered // a warning to be emitted which requires the microtask and next tick // queues to be drained again. @@ -236,19 +226,8 @@ function processPromiseRejections() { case kWarnWithErrorCodeUnhandledRejections: { const handled = process.emit('unhandledRejection', reason, promise); if (!handled) { - emitUnhandledRejectionWarning(uid, reason); - process.exitCode = 1; - } - break; - } - case kDefaultUnhandledRejections: { - const handled = process.emit('unhandledRejection', reason, promise); - if (!handled) { - emitUnhandledRejectionWarning(uid, reason); - if (!deprecationWarned) { - emitDeprecationWarning(); - deprecationWarned = true; - } + emitUnhandledRejectionWarning(uid, reason, true /* failing */); + process.exitCode = unhandledRejectionExitCode; } break; } diff --git a/test/es-module/test-esm-cjs-load-error-note.mjs b/test/es-module/test-esm-cjs-load-error-note.mjs index c0ac9393a8ddd5..3b8449ba4b9f60 100644 --- a/test/es-module/test-esm-cjs-load-error-note.mjs +++ b/test/es-module/test-esm-cjs-load-error-note.mjs @@ -17,6 +17,7 @@ const expectedNote = 'To load an ES module, ' + 'or use the .mjs extension.'; const expectedCode = 1; +const expectedUnhandledRejectionCode = 66; const pExport1 = spawn(process.execPath, [Export1]); let pExport1Stderr = ''; @@ -63,8 +64,7 @@ pImport2.stderr.on('data', (data) => { pImport2Stderr += data; }); pImport2.on('close', mustCall((code) => { - // As this exits normally we expect 0 - assert.strictEqual(code, 0); + assert.strictEqual(code, expectedUnhandledRejectionCode); assert.ok(!pImport2Stderr.includes(expectedNote), `${expectedNote} must not be included in ${pImport2Stderr}`); })); @@ -94,7 +94,7 @@ pImport4.on('close', mustCall((code) => { `${expectedNote} not found in ${pImport4Stderr}`); })); -// Must exit with zero and show note +// Must exit with 66 and show note const pImport5 = spawn(process.execPath, [Import5]); let pImport5Stderr = ''; pImport5.stderr.setEncoding('utf8'); @@ -102,7 +102,7 @@ pImport5.stderr.on('data', (data) => { pImport5Stderr += data; }); pImport5.on('close', mustCall((code) => { - assert.strictEqual(code, 0); + assert.strictEqual(code, expectedUnhandledRejectionCode); assert.ok(!pImport5Stderr.includes(expectedNote), `${expectedNote} must not be included in ${pImport5Stderr}`); })); diff --git a/test/message/promise_unhandled_warn_with_error.js b/test/message/promise_unhandled_warn_with_error.js index 0f22e8deeba653..adf9e112cd8f32 100644 --- a/test/message/promise_unhandled_warn_with_error.js +++ b/test/message/promise_unhandled_warn_with_error.js @@ -7,4 +7,4 @@ const assert = require('assert'); common.disableCrashOnUnhandledRejection(); Promise.reject(new Error('alas')); -process.on('exit', assert.strictEqual.bind(null, 1)); +process.on('exit', assert.strictEqual.bind(null, 66)); diff --git a/test/message/promise_unhandled_warn_with_error.out b/test/message/promise_unhandled_warn_with_error.out index b539adb2d1e769..3c214a9458840e 100644 --- a/test/message/promise_unhandled_warn_with_error.out +++ b/test/message/promise_unhandled_warn_with_error.out @@ -7,4 +7,4 @@ at * at * (Use `node --trace-warnings ...` to show where the warning was created) -*UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) \ No newline at end of file +*UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). This process will terminate with exit code 66. To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) \ No newline at end of file diff --git a/test/message/unhandled_promise_trace_warnings.out b/test/message/unhandled_promise_trace_warnings.out index c22dfbe0fcc8fa..5100eecc7e55e7 100644 --- a/test/message/unhandled_promise_trace_warnings.out +++ b/test/message/unhandled_promise_trace_warnings.out @@ -17,10 +17,6 @@ at * at * at * -(node:*) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. - at * - at * - at * (node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1) at handledRejection (internal/process/promises.js:*) at promiseRejectHandler (internal/process/promises.js:*) diff --git a/test/parallel/test-async-wrap-pop-id-during-load.js b/test/parallel/test-async-wrap-pop-id-during-load.js index bf75451817164b..ad4a4bbd96bfdd 100644 --- a/test/parallel/test-async-wrap-pop-id-during-load.js +++ b/test/parallel/test-async-wrap-pop-id-during-load.js @@ -19,7 +19,7 @@ const ret = spawnSync( ['--stack_size=150', __filename, 'async'], { maxBuffer: Infinity } ); -assert.strictEqual(ret.status, 0, +assert.strictEqual(ret.status, 66, `EXIT CODE: ${ret.status}, STDERR:\n${ret.stderr}`); const stderr = ret.stderr.toString('utf8', 0, 2048); assert.ok(!/async.*hook/i.test(stderr)); diff --git a/test/parallel/test-promises-unhandled-proxy-rejections.js b/test/parallel/test-promises-unhandled-proxy-rejections.js index 3a4f2d1ba8b3ac..c01ad401109e36 100644 --- a/test/parallel/test-promises-unhandled-proxy-rejections.js +++ b/test/parallel/test-promises-unhandled-proxy-rejections.js @@ -1,13 +1,9 @@ +// Flags: --unhandled-rejections=warn 'use strict'; const common = require('../common'); common.disableCrashOnUnhandledRejection(); -const expectedDeprecationWarning = ['Unhandled promise rejections are ' + - 'deprecated. In the future, promise ' + - 'rejections that are not handled will ' + - 'terminate the Node.js process with a ' + - 'non-zero exit code.', 'DEP0018']; const expectedPromiseWarning = ['Unhandled promise rejection. ' + 'This error originated either by throwing ' + 'inside of an async function without a catch ' + @@ -39,7 +35,6 @@ const thorny = new Proxy({}, { }); common.expectWarning({ - DeprecationWarning: expectedDeprecationWarning, UnhandledPromiseRejectionWarning: expectedPromiseWarning, }); diff --git a/test/parallel/test-promises-unhandled-rejections.js b/test/parallel/test-promises-unhandled-rejections.js index 86a4370c518c56..76964c417db1f2 100644 --- a/test/parallel/test-promises-unhandled-rejections.js +++ b/test/parallel/test-promises-unhandled-rejections.js @@ -1,3 +1,4 @@ +// Flags: --unhandled-rejections=warn 'use strict'; const common = require('../common'); const assert = require('assert'); @@ -714,7 +715,7 @@ asyncTest( done(); } emitWarning(...args); - }, 2); + }, 4); let timer = setTimeout(common.mustNotCall(), 10000); }, diff --git a/test/parallel/test-promises-unhandled-symbol-rejections.js b/test/parallel/test-promises-unhandled-symbol-rejections.js index 2851ce977fc4d0..cfcce5b148163d 100644 --- a/test/parallel/test-promises-unhandled-symbol-rejections.js +++ b/test/parallel/test-promises-unhandled-symbol-rejections.js @@ -1,14 +1,10 @@ +// Flags: --unhandled-rejections=warn 'use strict'; const common = require('../common'); common.disableCrashOnUnhandledRejection(); const expectedValueWarning = ['Symbol()']; -const expectedDeprecationWarning = ['Unhandled promise rejections are ' + - 'deprecated. In the future, promise ' + - 'rejections that are not handled will ' + - 'terminate the Node.js process with a ' + - 'non-zero exit code.', 'DEP0018']; const expectedPromiseWarning = ['Unhandled promise rejection. ' + 'This error originated either by throwing ' + 'inside of an async function without a catch ' + @@ -20,7 +16,6 @@ const expectedPromiseWarning = ['Unhandled promise rejection. ' + '(rejection id: 1)']; common.expectWarning({ - DeprecationWarning: expectedDeprecationWarning, UnhandledPromiseRejectionWarning: [ expectedValueWarning, expectedPromiseWarning diff --git a/test/parallel/test-promises-warning-on-unhandled-rejection.js b/test/parallel/test-promises-warning-on-unhandled-rejection.js index 596bf27ad1917d..f9707b80d3b5a9 100644 --- a/test/parallel/test-promises-warning-on-unhandled-rejection.js +++ b/test/parallel/test-promises-warning-on-unhandled-rejection.js @@ -1,4 +1,4 @@ -// Flags: --no-warnings +// Flags: --no-warnings --unhandled-rejections=warn 'use strict'; // Test that warnings are emitted when a Promise experiences an uncaught @@ -27,14 +27,10 @@ process.on('warning', common.mustCall((warning) => { ); break; case 2: - // One time deprecation warning, first unhandled rejection - assert.strictEqual(warning.name, 'DeprecationWarning'); - break; - case 3: // Number rejection error displayed. Note it's been stringified assert.strictEqual(warning.message, '42'); break; - case 4: + case 3: // Unhandled rejection warning (won't be handled next tick) assert.strictEqual(warning.name, 'UnhandledPromiseRejectionWarning'); assert( @@ -43,13 +39,13 @@ process.on('warning', common.mustCall((warning) => { `but did not. Had "${warning.message}" instead.` ); break; - case 5: + case 4: // Rejection handled asynchronously. assert.strictEqual(warning.name, 'PromiseRejectionHandledWarning'); assert(/Promise rejection was handled asynchronously/ .test(warning.message)); } -}, 6)); +}, 5)); const p = Promise.reject('This was rejected'); // Reject with a string setImmediate(common.mustCall(() => p.catch(() => { }))); diff --git a/test/sequential/test-inspector-async-call-stack-abort.js b/test/sequential/test-inspector-async-call-stack-abort.js index ce2b43cdf02e82..a72bb46c73d330 100644 --- a/test/sequential/test-inspector-async-call-stack-abort.js +++ b/test/sequential/test-inspector-async-call-stack-abort.js @@ -31,7 +31,7 @@ if (process.argv[2] === 'child') { const options = { encoding: 'utf8' }; const proc = spawnSync( process.execPath, ['--expose-internals', __filename, 'child'], options); - strictEqual(proc.status, 0); + strictEqual(proc.status, 66); strictEqual(proc.signal, null); strictEqual(proc.stderr.includes(eyecatcher), true); }