Nick Tomlin
Nick Tomlin

Reputation: 29241

Async stacktraces in node

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

Answers (1)

Nick Tomlin
Nick Tomlin

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

Related Questions