Repository navigation
Missing import trace from error stack when loading a module throws an error #46992
Description
Version
v16.16.0
Platform
Linux 5.4.0-139-generic #156-Ubuntu SM x86_64 GNU/Linux
Subsystem
modules
What steps will reproduce the bug?
Consider:
// foo.mjs
import './bar.mjs';// bar.mjs
throw new Error('bar failed');Run node foo.mjs.
How often does it reproduce? Is there a required condition?
Always.
What is the expected behavior?
The stack trace from the error should include a reference to foo.mjs line 1 showing where bar was imported from.
What do you see instead?
file:///.../bar.mjs:1
throw new Error('bar failed');
^
Error: bar failed
at file:///.../bar.mjs:1:7
at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
at async loadESM (node:internal/process/esm_loader:88:5)
at async handleMainPromise (node:internal/modules/run_main:61:12)Note that the stack trace does not mention foo anywhere. This is not helpful, especially if the error is specifically about how and when bar is imported. For example, it might be a singleton and you're mistakenly creating a duplicate by loading it from both import and require, or it might need something else to be set up on the global first and you're loading it in the wrong order.
The above stack is from node 16. Node 18 produces a shorter but similarly lacking stack:
file:///.../bar.mjs:1
throw new Error('bar failed');
^
Error: bar failed
at file:///.../bar.mjs:1:7
at ModuleJob.run (node:internal/modules/esm/module_job:194:25)Additional information
I am aware that import() and top-level await complicates this, since modules may be effectively loading in parallel (asynchronously behind the scenes). However, it is absolutely essential that this information is made available somehow.
If the output ends up being a list of all modules trying to load the offending module then great, as long as this list is in the order the imports were encountered then that should allow debugging this kind of thing.
At the moment when this occurs it's simply impossible to debug. Since import gets hoisted by the language you can't even add console.log statements around the place to trace what's happening like you can with require. Literally the only option to debug this is to comment out all the imports and re-enable one-by-one (recursively) until you locate the problem. And in practice this inevitably breaks something else, so it's completely unworkable.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsAwaiting Triage
Activity
benjamingr commentedon Mar 7, 2023
@nodejs/modules @nodejs/loaders
JakobJingleheimer commentedon Mar 8, 2023
JakobJingleheimer commentedon Mar 8, 2023
Gah, sorry, I misread my own output 🤦♂️
This may be fixed as part of #44710, which will most definitely change the stack-trace. I'll test there once all the tests are passing consistently.
GeoffreyBooth commentedon Mar 12, 2023
What are the stack traces produced for the equivalent test case in Deno and in Chrome?
SystemParadox commentedon Mar 16, 2023
This is the output from Deno (1.31.3), basically the same as Node:
Chrome is also the same, but the key difference is that with Chrome you can at least go to the network tab and look at the "Initiators" list to see why something is being loaded. It's really clumsy (you have to manually search for it in the network tab and you can't double-click anything) and it lacks line numbers, but it's a start.
I don't use Deno but if a Deno user wants to raise a corresponding bug with them and link it here that would probably be a good idea.
GeoffreyBooth commentedon Mar 16, 2023
If none of the runtimes are doing this, then it's a feature request rather than a bug. For me it also raises the question of what a stack trace is; I would think it should go to the line that threw the exception, as it does in the current traces, rather than the module that imported the line that threw the exception.
SystemParadox commentedon Mar 16, 2023
It's a very serious and major regression when compared with commonJS. People are shifting to pure ESM now and it's literally impossible to debug this kind of thing. This really needs to be sorted ASAP.
Obviously that should be the top of the stack. But the stack should not stop there, it needs context. Even if due to the technicalities of async module loading it might be requested from multiple places that doesn't change anything. If a module throws an exception we need to see why it was loaded and in what order.
23 remaining items
SystemParadox commentedon Jan 23, 2026
One thing that you can do right now is this:
It is very clumsy, but at least makes debugging imports possible by giving a way to add log messages within the import flow that don't get hoisted out of place.
This is documented at https://nodejs.org/api/esm.html#data-imports which is helpful.
mattskel commentedon Feb 24, 2026
I have opened a PR for this issue. Codecov and linting errors are resolved. Happy to discuss the implementation further.
SystemParadox commentedon Feb 27, 2026
Amazing! Is it possible to include the line number of the import statement?
SystemParadox commentedon Feb 27, 2026
It also feels like this duplicates the filename unnecessarily. Possibly we don't need to include the module where the error was because it'll be in the stack trace?
Maybe something more like this?
Or this?
Also, the indentation should match stack traces, currently it's 2 spaces which is inconsistent.
esm: add import trace for evaluation errors
esm: add line and column
mattskel commentedon Mar 2, 2026
@SystemParadox see recent changes
SystemParadox commentedon Mar 2, 2026
That looks much neater.
I notice it doesn't support source maps - I've got a typescript project that gets compiled to
distwith esbuild and the error stack showssrc/xxx.ts, but the import trace showsdist/node/chunk-xxxxx.js.I'm not entirely sure what sourcemaps would even mean in this situation, maybe they don't even make sense for the import trace? If so then I'd suggest we should include the unmapped error line in the import trace, otherwise you can't see where the dist stack ends.
github-actions commentedon Jul 20, 2026
This issue has been marked as stale due to 90 days of inactivity.
It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.
SystemParadox commentedon Jul 20, 2026
This is still relevant, we just have to figure out how best to show the trace when source maps are involved.