Skip to content

module: improve error message for top-level await in CommonJS - #55874

Merged
nodejs-github-bot merged 14 commits into
nodejs:mainfrom
mertcanaltin:dev-55776
Jun 24, 2025
Merged

module: improve error message for top-level await in CommonJS#55874
nodejs-github-bot merged 14 commits into
nodejs:mainfrom
mertcanaltin:dev-55776

Conversation

@mertcanaltin

Copy link
Copy Markdown
Member

Added a specific error message for using top-level await in CommonJS modules. The error now suggests using ESM or wrapping await in an async function for clarity.

Fixes: #55776

@nodejs-github-botnodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. vm Issues and PRs related to the vm subsystem. labels Nov 15, 2024
@codecov

codecovBot commented Nov 15, 2024

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 82.60870% with 4 lines in your changes missing coverage. Please review.

Project coverage is 90.08%. Comparing base (968e2f4) to head (ad40103).
Report is 153 commits behind head on main.

Files with missing linesPatch %Lines
src/module_wrap.cc69.23%2 Missing and 2 partials ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #55874 +/- ##
==========================================
- Coverage 90.21% 90.08% -0.13% 
==========================================
Files 635 640 +5 Lines 187580 188384 +804 Branches 36853 36937 +84 ==========================================
+ Hits 169231 169712 +481 - Misses 11108 11396 +288 - Partials 7241 7276 +35 
Files with missing linesCoverage Δ
lib/internal/modules/esm/module_job.js97.48% <100.00%> (+0.26%)⬆️
src/module_wrap.h0.00% <ø> (ø)
src/module_wrap.cc72.59% <69.23%> (+0.15%)⬆️

... and 124 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@GeoffreyBoothGeoffreyBooth left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for tackling this! I’d love to see this issue addressed.

Comment threadsrc/node_contextify.cc Outdated
Comment threadsrc/node_contextify.cc Outdated
@mertcanaltinmertcanaltin added the help wanted Issues that need assistance from volunteers or PRs that need help to proceed. label Nov 27, 2024
@mertcanaltin
mertcanaltinforce-pushed the dev-55776 branch 3 times, most recently from 76f87e4 to c9531efCompareNovember 29, 2024 21:21
@mertcanaltinmertcanaltin added review wanted PRs that need reviews. and removed help wanted Issues that need assistance from volunteers or PRs that need help to proceed. labels Nov 30, 2024

@GeoffreyBoothGeoffreyBooth left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking another pass at this. I mention this in the particular notes, but in general:

  1. Please don't change lines based on personal preference or readability. We value the ability for git blame to point directly to the commit/PR that added or meaningfully changed a particular line, and that ability is diluted when unrelated PRs rewrite lines of code.

  2. Very few, if any, tests should change as a result of your PR: only tests specifically related to top-level await in CommonJS. Any other tests that fail as a result of your changes mean that there's a bug in the implementation that needs to be addressed. My guess is that the bug is that the new code isn't checking that the error message being thrown is specifically about top-level await. If there aren't currently tests for top-level await in CommonJS and your new error message, one or more should be added.

This is close; another pass and I think you may get it there. Thanks for your effort!

Comment threadsrc/node_contextify.cc Outdated
Comment threadsrc/node_contextify.cc Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
@mertcanaltin

Copy link
Copy Markdown
MemberAuthor

Thanks for taking another pass at this. I mention this in the particular notes, but in general:

  1. Please don't change lines based on personal preference or readability. We value the ability for git blame to point directly to the commit/PR that added or meaningfully changed a particular line, and that ability is diluted when unrelated PRs rewrite lines of code.
  2. Very few, if any, tests should change as a result of your PR: only tests specifically related to top-level await in CommonJS. Any other tests that fail as a result of your changes mean that there's a bug in the implementation that needs to be addressed. My guess is that the bug is that the new code isn't checking that the error message being thrown is specifically about top-level await. If there aren't currently tests for top-level await in CommonJS and your new error message, one or more should be added.

This is close; another pass and I think you may get it there. Thanks for your effort!

thank you very much for the review, I will be sending a new commit following what you said

@mertcanaltin

Copy link
Copy Markdown
MemberAuthor

Greetings, when I did as you suggested to cover only the top level error, I saw that all tests passed except 1 test

@GeoffreyBoothGeoffreyBooth left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is much better. What’s the one test that is failing?

Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
Comment threadtest/es-module/test-esm-detect-ambiguous.mjs Outdated
@GeoffreyBoothGeoffreyBooth added module Issues and PRs related to the module subsystem. esm Issues and PRs related to the ECMAScript Modules implementation. labels Dec 2, 2024
@mertcanaltin

Copy link
Copy Markdown
MemberAuthor

Thanks, this is much better. What’s the one test that is failing?

I saw wrong, all the tests passed my local. 🙏

@GeoffreyBooth

Copy link
Copy Markdown
Member

I saw wrong, all the tests passed my local. 🙏

There’s a test failing in here, in the relevant file: https://github.com/nodejs/node/actions/runs/12133213207/job/33828380981?pr=55874

@cjihrig why isn’t the CI output showing me which test is failing within that file?

@cjihrig

Copy link
Copy Markdown
Contributor

why isn’t the CI output showing me which test is failing within that file?

No clue. But the output is incomplete. There is no summary at the end either. It looks like maybe the Python runner killed the Node process or otherwise truncated its output.

@cjihrig

Copy link
Copy Markdown
Contributor

@GeoffreyBooth just FYI - IIRC, you asked me a similar question in Slack last year. There was output truncated only in GitHub Actions. Then you asked me to find a test run where truncation was happening without the use of node:test, and I did. I believe @MoLow was a part of that thread as well.

@GeoffreyBooth

Copy link
Copy Markdown
Member

IIRC, you asked me a similar question in Slack last year.

Yes, last August: https://openjs-foundation.slack.com/archives/C019Y2T6STH/p1691860008875109. I’m lucky if I can remember last month, much less last year. Sorry to repeat. I also opened #49120 but that got closed.

@mertcanaltin

Copy link
Copy Markdown
MemberAuthor

I saw wrong, all the tests passed my local. 🙏

There’s a test failing in here, in the relevant file: https://github.com/nodejs/node/actions/runs/12133213207/job/33828380981?pr=55874

@cjihrig why isn’t the CI output showing me which test is failing within that file?

yes, ı view my local enviorement this test is fail

testattest/es-module/test-esm-detect-ambiguous.mjs:367:5doesnotwarnwhentherearenopackage.json(63.625667ms)AssertionError[ERR_ASSERTION]: Expectedvaluestobestrictly equal:
+actual-expected+"(node:18411) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/mert/Desktop/openSource/node/test/fixtures/es-modules/loose.js is not specified and it doesn't parse as CommonJS.\n"++'Reparsing as ES module because module syntax was detected. This incurs a performance overhead.\n'++'To eliminate this warning, add "type": "module" to /Users/mert/package.json.\n'++'(Use `node --trace-warnings ...` to show where the warning was created)\n'-''atTestContext.<anonymous>(file:///Users/mert/Desktop/openSource/node/test/es-module/test-esm-detect-ambiguous.mjs:372:7)atprocess.processTicksAndRejections(node:internal/process/task_queues:105:5)atasyncTest.run(node:internal/test_runner/test:932:9)atasyncPromise.all(index3)atasyncSuite.run(node:internal/test_runner/test:1310:7)atasyncPromise.all(index6)atasyncSuite.run(node:internal/test_runner/test:1310:7)atasyncstartSubtestAfterBootstrap(node:internal/test_runner/harness:297:3){
generatedMessage: true,code: 'ERR_ASSERTION',actual: '(node:18411) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///Users/mert/Desktop/openSource/node/test/fixtures/es-modules/loose.js is not specified and it doesn\'t parse as CommonJS.\nReparsing as ES module because module syntax was detected. This incurs a performance overhead.\nTo eliminate this warning, add "type": "module" to /Users/mert/package.json.\n(Use `node --trace-warnings ...` to show where the warning was created)\n',expected: '',operator: 'strictEqual'}

@mertcanaltin

Copy link
Copy Markdown
MemberAuthor

greetings I tried to make the error message more descriptive, in addition I tried to throw it when there is a file that cannot be successfully parsed as ESM

@mertcanaltin

Copy link
Copy Markdown
MemberAuthor

hello, I would be very grateful if you could look here when you are available, thank you very much @GeoffreyBooth

isCommonJSGlobalLikeNotDefinedError(e.message)) {

if (hasTopLevelAwait) {
e.message = `ERR_AMBIGUOUS_MODULE_SYNTAX: This file cannot be parsed as either CommonJS or ES Module. CommonJS error: await is only valid in async functions. ES Module error: require is not defined in ES module scope. If you meant to use CommonJS, wrap top-level await in async function. If you meant to use ESM, do not use require().`;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'm a little confused here -- the code mostly looks good, but maybe we can spell out which condition exactly is supposed to trigger this error, and be specific about the error message? Like, it's odd to describe multiple different errors here, although I kind of see where you're coming from

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @addaleax,

You're absolutely right - the message is trying to do too much.

The specific condition is: file has both require() and top-level await, creating an ambiguous parsing situation.

How about we simplify the error message to focus on the immediate issue:

ERR_AMBIGUOUS_MODULE_SYNTAX: Cannot use 'require()' and top-level 'await'in the same file.
Choose either CommonJS (remove await, use async functions) or ES Module (remove require, use import).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it can be parsed as ESM, just not successfully evaluated as ESM because require is undefined. It can't be successfully parsed as CommonJS because of the top-level await. So I'm not sure if "ambiguous" is the right word; it's more that it's so unclear what the user intended that we can't reasonably make a guess.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically it can be parsed as ESM, just not successfully evaluated as ESM because require is undefined. It can't be successfully parsed as CommonJS because of the top-level await. So I'm not sure if "ambiguous" is the right word; it's more that it's so unclear what the user intended that we can't reasonably make a guess.

Yes, I agree with what you said, I think a message like this would be more appropriate.

Cannotdetermineintendedmoduleformat.Filecontainsboth'require()'andtop-level'await'.ForCommonJS: wrapawaitinasyncfunction.ForESModule: replacerequire()withimport.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume when you write For CommonJS: what you mean is, If you intended this file to be CommonJS, and likewise for For ES Module:. So maybe:

Cannot determine intended module format because both require() and top-level await are present. If the code is intended to be CommonJS, wrap await in an async function. If the code is intended to be an ES module, replace require() with import.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks I updated

@anonriganonrig added author ready PRs that have at least one approval, no pending requests for changes, and a CI started. request-ci Add this label to start a Jenkins CI on a PR. labels Jun 24, 2025
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Jun 24, 2025
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@anonriganonrig added commit-queue Add this label to land a pull request using GitHub Actions. commit-queue-squash Add this label to instruct the Commit Queue to squash all the PR commits into the first one. labels Jun 24, 2025
@nodejs-github-botnodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Jun 24, 2025
@nodejs-github-bot
nodejs-github-bot merged commit 6ce6fdb into nodejs:mainJun 24, 2025
68 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in 6ce6fdb

targos pushed a commit that referenced this pull request Jul 3, 2025
PR-URL: #55874Fixes: #55776
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Jul 21, 2025
PR-URL: #55874Fixes: #55776
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
aduh95 pushed a commit that referenced this pull request Jul 24, 2025
PR-URL: #55874Fixes: #55776
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
@coderabbitaicoderabbitaiBot mentioned this pull request Apr 16, 2026
2 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author readyPRs that have at least one approval, no pending requests for changes, and a CI started.c++Issues and PRs that require attention from people who are familiar with C++.commit-queue-squashAdd this label to instruct the Commit Queue to squash all the PR commits into the first one.esmIssues and PRs related to the ECMAScript Modules implementation.moduleIssues and PRs related to the module subsystem.needs-ciPRs that need a full CI run.review wantedPRs that need reviews.vmIssues and PRs related to the vm subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

detect-module: confusing error when parsing a CommonJS module with top-level await

7 participants

@mertcanaltin@GeoffreyBooth@cjihrig@nodejs-github-bot@joyeecheung@addaleax@anonrig