Skip to content

Missing import trace from error stack when loading a module throws an error #46992

Description

@SystemParadox

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.

Activity

  1. benjamingr commented on Mar 7, 2023

    @benjamingr
    Member

    @nodejs/modules @nodejs/loaders

  2. JakobJingleheimer commented on Mar 8, 2023

    @JakobJingleheimer
  3. added
    esmIssues and PRs related to the ECMAScript Modules implementation.
    loadersIssues and PRs related to ES module loaders.
    confirmed-bugIssues and PRs for confirmed bugs.
    on Mar 8, 2023
  4. JakobJingleheimer commented on Mar 8, 2023

    @JakobJingleheimer
    Member

    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.

  5. GeoffreyBooth commented on Mar 12, 2023

    @GeoffreyBooth
    Member

    in practice this inevitably breaks something else, so it’s completely unworkable.

    What are the stack traces produced for the equivalent test case in Deno and in Chrome?

  6. SystemParadox commented on Mar 16, 2023

    @SystemParadox
    Author

    This is the output from Deno (1.31.3), basically the same as Node:

    error: Uncaught Error: bar failed
    throw new Error('bar failed');
          ^
        at file:///.../bar.mjs:1:7
    

    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.

  7. GeoffreyBooth commented on Mar 16, 2023

    @GeoffreyBooth
    Member

    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.

  8. added
    feature requestIssues requesting new Node.js features.
    and removed
    loadersIssues and PRs related to ES module loaders.
    on Mar 16, 2023
  9. SystemParadox commented on Mar 16, 2023

    @SystemParadox
    Author

    If none of the runtimes are doing this, then it's a feature request rather than a bug.

    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.

    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.

    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.

  10. 23 remaining items

  11. SystemParadox commented on Jan 23, 2026

    @SystemParadox
    Author

    One thing that you can do right now is this:

    import 'data:text/javascript,console.log("hello");';
    

    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.

  12. mattskel commented on Feb 24, 2026

    @mattskel

    I have opened a PR for this issue. Codecov and linting errors are resolved. Happy to discuss the implementation further.

  13. SystemParadox commented on Feb 27, 2026

    @SystemParadox
    Author

    Amazing! Is it possible to include the line number of the import statement?

  14. SystemParadox commented on Feb 27, 2026

    @SystemParadox
    Author
    Import trace:
      file:///home/simon/github/node/baz.mjs imported by file:///home/simon/github/node/bar.mjs
      file:///home/simon/github/node/bar.mjs imported by file:///home/simon/github/node/foo.mjs
    

    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?

    Imported by:
        file:///home/simon/github/node/bar.mjs:1:1
        file:///home/simon/github/node/foo.mjs:1:1
    

    Or this?

    Import trace:
      file:///home/simon/github/node/baz.mjs
      imported by file:///home/simon/github/node/bar.mjs:1:1
      imported by file:///home/simon/github/node/foo.mjs:1:1
    

    Also, the indentation should match stack traces, currently it's 2 spaces which is inconsistent.

  15. mattskel commented on Mar 2, 2026

    @mattskel

    @SystemParadox see recent changes

    • Remove duplicate filenames.
    • Add line and column info.
    • Format indent.
  16. SystemParadox commented on Mar 2, 2026

    @SystemParadox
    Author

    That looks much neater.

    I notice it doesn't support source maps - I've got a typescript project that gets compiled to dist with esbuild and the error stack shows src/xxx.ts, but the import trace shows dist/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.

  17. github-actions commented on Jul 20, 2026

    @github-actions
    Contributor

    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.

  18. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jul 20, 2026
  19. SystemParadox commented on Jul 20, 2026

    @SystemParadox
    Author

    This is still relevant, we just have to figure out how best to show the trace when source maps are involved.

  20. removed
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    esmIssues and PRs related to the ECMAScript Modules implementation.feature requestIssues requesting new Node.js features.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions