Reputation: 29241
I'm a little surprised I haven't been bitten by this more, but node errors created in a callback that is executed in an another tick do not have a sane stack trace.
E.g.
function base (cb) {
process.nextTick(() => {
throw new Error("I am unhelpful")
}, 1000)
}
function top (cb) {
base(cb)
}
top(() => {})
Results in:
Error: I am unhelpful
at /Users/me/stacktrace.js:45:11
at _combinedTickCallback (internal/process/next_tick.js:135:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
at Function.Module.runMain (module.js:607:11)
at startup (bootstrap_node.js:158:16)
at bootstrap_node.js:575:3
This is particuarly bad when the exception happens in the callback/promise from a library that performs asynchronous actions, since there's no easy path back up the trace to find the offending code. Imagine a service call that involves a controller, some helper modules and a third party library.
My solution for this so far is to create an error for the potential failure case in the current tick, and then pass it through if there is an error:
function base (cb) {
let potentialError = new Error('oh noes')
process.nextTick((err) => {
potentialError.message = err.message
throw potentialError
}, 1000)
}
This gives me a stacktrace that actually includes the call chain:
Error: oh noes
at base (/Users/me/stacktrace.js:47:24)
at top (/Users/me/stacktrace.js:43:3)
at Object.<anonymous> (/Users/me/stacktrace.js:53:1)
I know of modules like superstack but they monkey patch error and don't seem to work with recent versions of node.
Core itself just built in support for async stack traces but this is an experimental/dev only feature not recommended for production.
Is there a better way to achieve what I want?
Upvotes: 6
Views: 816
Reputation: 29241
This is now available for async
functions (functions using the async
keyword, not all asynchronous functions) in node 12.x via the --async-stack-traces
command line flag. Hopefully this will eventually be standardized everywhere.
More details in the release notes and the feature document.
Non async
functions currently are not supported, alas.
Upvotes: 2