diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js index 46cf9f95407a..c939e3842937 100644 --- a/lib/internal/process/execution.js +++ b/lib/internal/process/execution.js @@ -17,6 +17,7 @@ const { ERR_INVALID_ARG_TYPE, ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET, }, + fatalExceptionStackEnhancers, } = require('internal/errors'); const { validateFunction } = require('internal/validators'); const { pathToFileURL } = require('internal/url'); @@ -174,6 +175,10 @@ function createOnGlobalUncaughtException() { // call that threw and was never cleared. So clear it now. clearDefaultTriggerAsyncId(); + if (er != null && typeof er === 'object') { + fatalExceptionStackEnhancers.beforeInspector(er); + } + const type = fromPromise ? 'unhandledRejection' : 'uncaughtException'; process.emit('uncaughtExceptionMonitor', er, type); // Primary callback (e.g., domain) has priority and always handles the exception diff --git a/test/parallel/test-process-uncaught-exception-enhanced-stack.js b/test/parallel/test-process-uncaught-exception-enhanced-stack.js new file mode 100644 index 000000000000..c45ba9cb0a78 --- /dev/null +++ b/test/parallel/test-process-uncaught-exception-enhanced-stack.js @@ -0,0 +1,47 @@ +'use strict'; +const common = require('../common'); +const assert = require('node:assert'); +const EventEmitter = require('node:events'); + +class CustomEmitter extends EventEmitter {} + +const ee = new EventEmitter(); +const customEE = new CustomEmitter(); + +let monitorCount = 0; +let uncaughtCount = 0; + +process.on('uncaughtExceptionMonitor', common.mustCall((err, origin) => { + assert.strictEqual(origin, 'uncaughtException'); + monitorCount++; + if (monitorCount === 1) { + assert.match(err.stack, /Emitted 'error' event at:/); + assert.match(err.stack, /at emitPlainError/); + } else if (monitorCount === 2) { + assert.match(err.stack, /Emitted 'error' event on CustomEmitter instance at:/); + assert.match(err.stack, /at emitCustomClassError/); + } +}, 2)); + +process.on('uncaughtException', common.mustCall((err, origin) => { + assert.strictEqual(origin, 'uncaughtException'); + uncaughtCount++; + if (uncaughtCount === 1) { + assert.match(err.stack, /Emitted 'error' event at:/); + assert.match(err.stack, /at emitPlainError/); + process.nextTick(emitCustomClassError); + } else if (uncaughtCount === 2) { + assert.match(err.stack, /Emitted 'error' event on CustomEmitter instance at:/); + assert.match(err.stack, /at emitCustomClassError/); + } +}, 2)); + +function emitPlainError() { + ee.emit('error', new Error('plain error')); +} + +function emitCustomClassError() { + customEE.emit('error', new Error('custom class error')); +} + +emitPlainError();