Skip to content

Dynamic import does not show location of SyntaxError #49441

Description

@coreyfarrell
// syntax-error.mjs
console.log([
  'str1'
  'str2'
]);

Perform a dynamic import with node -e "import('./syntax-error.mjs').catch(console.error)", the output does not help identify the error and could be seen by users as if node.js itself had a SyntaxError:

SyntaxError: Unexpected string
    at Loader.moduleStrategy (internal/modules/esm/translators.js:83:18)
    at async link (internal/modules/esm/module_job.js:37:21)

Now create and run a loader.mjs script:

import './syntax.mjs';

Running loader.mjs produces useful output:

file:///usr/src/npm/failures/syntax-error.mjs:3
	'str2'
	^^^^^^

SyntaxError: Unexpected string
    at Loader.moduleStrategy (internal/modules/esm/translators.js:83:18)

I was not able to find a way to catch the error of a dynamic import and show the complete SyntaxError. Interestingly node --unhandled-rejections=strict -e "import('./syntax-error.mjs')" or running await import('./syntax-error.mjs') from node --experimental-repl-await both display the location of the SyntaxError.

Activity

  1. hybrist commented on Jan 16, 2020

    @hybrist
    Contributor

    This is something we unfortunately inherited from Chrome/V8. You can try the following in the browser devtools:

    await import('data:text/javascript,<not valid syntax>')

    We may be able to patch the stack even further. The main issue is that import() actually makes these errors easily observable. So the fact that err.stack is "weird" and doesn't properly start with the usual prefix would leak.

  2. ljharb commented on Jan 16, 2020

    @ljharb
    SponsorMember

    That leak will pose a problem as the error stacks proposal attempts to advance; it would be exceedingly ideal to avoid creating new observable stack trace formats/contents.

  3. hybrist commented on Jan 16, 2020

    @hybrist
    Contributor

    One thing I'd love to see is to have syntax errors from module executions include the parsed module as the top frame. But I can understand why that may be awkward (there's no actual stack frame running that code).

  4. ljharb commented on Jan 16, 2020

    @ljharb
    SponsorMember

    My primary concern is avoiding new kinds of traces; id rather see no error than a new kind of error. A very close second is seeing useful ones :-)

  5. hybrist commented on Jan 16, 2020

    @hybrist
    Contributor

    Somewhat OT: The above stack traces are a great reason to invest into moving even more of the loader into C++. None of these frames are remotely helpful to understand what's going on:

    SyntaxError: Unexpected string
        at Loader.moduleStrategy (internal/modules/esm/translators.js:83:18)
        at async link (internal/modules/esm/module_job.js:37:21)
    

    Compared with the - theoretical - error showing the syntax error location as the top frame and without any JS frames from loader internals:

    SyntaxError: Unexpected string
        at file:///path/to/project/syntax-error.mjs:4:2
    
  6. addaleax commented on Jan 16, 2020

    @addaleax
    Member

    @jkrems If the issue is that there are C++ frames in the middle of a stack, cutting it off at some point, I’d prefer to come up with a solution that provides the full stack trace rather than moving more code into C++…

  7. hybrist commented on Jan 16, 2020

    @hybrist
    Contributor

    I think to me it's related to a direct comparison with browser stacks which is "suddenly" a thing for modules. With most other APIs (from setTimeout() to require()), there's user code calling JavaScript functions. So seeing JS frames of node's implementation doesn't feel too weird to me.

    But module execution feels different. I didn't create a module job, I ran a file. In the browser, it wouldn't show me stack frames from network requests and other kinds of orchestration needed to run the file. So seeing that in node appears inconsistent.

  8. addaleax commented on Jan 16, 2020

    @addaleax
    Member

    @jkrems Right, but I feel like “move code to a different language” is ultimately not a good solution to stack traces not being pretty enough – I’d rather find a way to provide better stack traces. In my opinion, we already have far too much module-related C++ code that would be more accessible when written in JS.

  9. GeoffreyBooth commented on Jan 16, 2020

    @GeoffreyBooth
    Member

    It’s always annoyed me that Node internals appear in stack traces. I wish we could just filter them out, or blackbox them, maybe by default which could be changed via a flag.

  10. hybrist commented on Jan 16, 2020

    @hybrist
    Contributor

    @addaleax That's definitely fair. Maybe what's missing is some API that allows us to start module execution with a "fresh stack" even when there's technically active JS frames. Feels like something that'd need V8 support if they would also appear "properly" within the module execution itself (try/catch)..?

    It’s always annoyed me that Node internals appear in stack traces. I wish we could just filter them out, or blackbox them, maybe by default which could be changed via a flag.

    @GeoffreyBooth I generally disagree. Seeing frames for the express subclass of http.Server but not for the parts that happen to be from the base class feels super confusing. In most cases having node's own code appear is pretty valuable imo.

  11. ljharb commented on Jan 16, 2020

    @ljharb
    SponsorMember

    The function implementation hiding proposal, which allows functions to opt out of stack frames, may provide a JS-native solution to this problem.

  12. hybrist commented on Jan 16, 2020

    @hybrist
    Contributor

    The function implementation hiding proposal, which allows functions to opt out of stack frames, may provide a JS-native solution to this problem.

    Maybe. Though it feels a bit brittle. I assume it would mean that we'd have to manually hide each individual function that makes up our implementation and be careful that we never have Array.forEach & friends on the stack. Possible but a bit too easy to break by accident for my taste.

  13. coreyfarrell commented on Jan 20, 2020

    @coreyfarrell
    MemberAuthor

    I can understand arguments for and against node.js internals appearing in the stack, but that's not why I opened this issue. My concern is not so much the extra information, it's the lack of necessary information. Filtering out node.js internals will not tell me where the syntax error occurred. Normally I use a linter but in some cases when testing a specific issue I might run the tests manually and bypass the lint step (that's how I found this issue).

    I know @jkrems suggested that this could be a v8 issue but I find it interesting that REPL top-level await and strict unhandled rejections both display the necessary information. This tells me that the information exists as part of the error object at some point. Seems like that information can only be displayed by the default uncaughtException handler?

    function example() {
      throw new Error('example');
    }
    
    if (process.env.CATCH_ERROR) {
      try {
        example();
      } catch (error) {
        console.error(error);
      }
    } else {
      example();
    }

    Running this with CATCH_ERROR=1 removes the source output though in this example it's less of an issue as the stack trace contains a line referencing the location of throw in example().

  14. hybrist commented on Jan 20, 2020

    @hybrist
    Contributor

    Yes, it can “always” appear on crashes and that’s definitely a good improvement. The issue is that it won’t appear on things that don’t crash or where errors are sent to reporting tools (e.g. log files). Making it part of the default unhandled rejection logging is a good quick fix though!

  15. benbucksch commented on May 23, 2020

    @benbucksch
    Contributor

    This bug is a big obstacle for me during development. I need to load apps dynamically, so any syntax error in any of my code will now not show anymore. It's very difficult to find errors without knowing where exactly the error is. This is completely unworkable.

    My workaround is to manually add hardcoded import statements to the files that I develop on at a given time. But that's cumbersome, tedious, and I need to do it manually every time. All the time. And this is only after I found the cause of why the stack is wrong. Many other developers would not even know why the stack is mis-attributing and would just tear their hairs without ever knowing why it failed.

    As @coreyfarrell said, because stacks are so important, even async/await processing impl found some way to preserve the stack, so I assume there must be some kind of workaround possible for this problem as well.

    This bug here is a serious problem for any usage of import().

  16. 21 remaining items

  17. added
    promisesIssues and PRs related to ECMAScript promises.
    errorsIssues and PRs related to JavaScript errors originating in Node.js core.
    esmIssues and PRs related to the ECMAScript Modules implementation.
    on Sep 1, 2023
  18. adrian-balan-mindit commented on Aug 13, 2024

    @adrian-balan-mindit
  19. rdev06 commented on Mar 24, 2025

    @rdev06
  20. nicolo-ribaudo commented on Mar 24, 2025

    @nicolo-ribaudo
  21. github-actions commented on Jun 27, 2026

    @github-actions
    Contributor

    This issue has been marked as stale due to 210 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.

  22. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jun 27, 2026
  23. EricMCornelius commented on Jun 27, 2026

    @EricMCornelius

    Still relevant.

  24. removed
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jun 28, 2026
  25. github-actions commented on Sep 27, 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.

  26. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Sep 27, 2026
  27. EricMCornelius commented on Sep 27, 2026

    @EricMCornelius

    Still relevant.

  28. removed
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Sep 28, 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

    errorsIssues and PRs related to JavaScript errors originating in Node.js core.esmIssues and PRs related to the ECMAScript Modules implementation.promisesIssues and PRs related to ECMAScript promises.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions