Title: v26: top-level `await import()` of an evaluating-async module resolves before the module runs (TDZ ReferenceError) · Issue #66806 · nodejs/node · GitHub
Open Graph Title: v26: top-level `await import()` of an evaluating-async module resolves before the module runs (TDZ ReferenceError) · Issue #66806 · nodejs/node
X Title: v26: top-level `await import()` of an evaluating-async module resolves before the module runs (TDZ ReferenceError) · Issue #66806 · nodejs/node
Description: Version v26.12.0 (V8 14.6.202.34-node.39). It works in v24.22.0 (V8 13.6) and v25.9.0 (V8 14.1.146.11). Deno 2.9.7 (V8 15.0.245.2) fails the same way, so the bug looks like it is in V8. Platform Linux 6.8.0-147-generic x86_64 GNU/Linux S...
Open Graph Description: Version v26.12.0 (V8 14.6.202.34-node.39). It works in v24.22.0 (V8 13.6) and v25.9.0 (V8 14.1.146.11). Deno 2.9.7 (V8 15.0.245.2) fails the same way, so the bug looks like it is in V8. Platform Li...
X Description: Version v26.12.0 (V8 14.6.202.34-node.39). It works in v24.22.0 (V8 13.6) and v25.9.0 (V8 14.1.146.11). Deno 2.9.7 (V8 15.0.245.2) fails the same way, so the bug looks like it is in V8. Platform Li...
Opengraph URL: https://github.com/nodejs/node/issues/66806
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"v26: top-level `await import()` of an evaluating-async module resolves before the module runs (TDZ ReferenceError)","articleBody":"### Version\n\nv26.12.0 (V8 14.6.202.34-node.39). It works in v24.22.0 (V8 13.6) and v25.9.0 (V8 14.1.146.11). Deno 2.9.7 (V8 15.0.245.2) fails the same way, so the bug looks like it is in V8.\n\n### Platform\n\n```text\nLinux 6.8.0-147-generic x86_64 GNU/Linux\n```\n\n### Subsystem\n\nesm / V8 module evaluation (top-level await + dynamic `import()`)\n\n### What steps will reproduce the bug?\n\n```sh\nmkdir tla-tdz && cd tla-tdz\ncat > main.mjs \u003c\u003c'JS'\nimport { a } from './a.mjs';\nimport { x } from './b.mjs';\nconsole.log('ok', a(), x);\nJS\ncat > a.mjs \u003c\u003c'JS'\nawait import('./d.mjs');\nconst { x } = await import('./b.mjs'); // b is evaluating-async here\nexport const a = () => x;\nJS\ncat > b.mjs \u003c\u003c'JS'\nimport { c } from './c.mjs';\nexport const x = c + 1;\nJS\ncat > c.mjs \u003c\u003c'JS'\nawait new Promise(resolve => setTimeout(resolve, 10));\nexport const c = 1;\nJS\necho 'export const d = 0;' > d.mjs\nnode main.mjs\n```\n\nHere is what happens. `main` evaluates `a` first. `a` suspends on its first `await import()`, so `main` goes on to evaluate `b`. `b` depends on `c`, and `c` has a top-level `await`, so `b` becomes *evaluating-async*. Then `a` resumes and runs `await import('./b.mjs')` while `b` has not executed yet.\n\n### How often does it reproduce? Is there a required condition?\n\nIt fails every time. It needs a top-level `await import()` of a module that another branch of the same graph has already started evaluating, so that the module is *evaluating-async*. The module also must not be the cycle root of a capability that already exists.\n\n### What is the expected behavior? Why is that the expected behavior?\n\n```text\nok 2 2\n```\n\nNode 24.22.0, Node 25.9.0 and Bun 1.4.3 all print this. In ECMA-262 [Evaluate()](https://tc39.es/ecma262/#sec-moduleevaluation), `b` is *evaluating-async*, so `module` is set to `b.[[CycleRoot]]`, which is `b`. Its `[[TopLevelCapability]]` is empty, so a new capability is created. `InnerModuleEvaluation` then returns immediately, and because the status is not *evaluated* the promise is left pending. The promise is resolved only by [AsyncModuleExecutionFulfilled](https://tc39.es/ecma262/#sec-async-module-execution-fulfilled) or its `GatherAvailableAncestors` loop, after `b` has executed. `ContinueDynamicImport` resolves `import()` with the namespace only once that promise fulfils.\n\n### What do you see instead?\n\n```text\nfile:///…/a.mjs:2\nconst { x } = await import('./b.mjs'); // b is evaluating-async here\n ^\n\nReferenceError: Cannot access 'x' before initialization\n at file:///…/a.mjs:2:9\n\nNode.js v26.12.0\n```\n\nThe `import()` promise resolves with the namespace of a module whose body has not run yet.\n\n### Additional information\n\nWe hit this in a real application: [link-assistant/hive-mind#3469](https://github.com/link-assistant/hive-mind/issues/3469). 43 top-level `await import()` calls in its `src/` threw TDZ errors once a host moved to Node 26, while CI on Node 24 stayed green. The reproduction is also in [`experiments/node26-tla-tdz`](https://github.com/link-assistant/hive-mind/tree/issue-3469-80e8383bb928/experiments/node26-tla-tdz).\n\n**Workaround:** replace the top-level `await import('./b.mjs')` with a static `import * as b from './b.mjs'`. A static import is part of the graph, so it is evaluated before the importer's body on every runtime.\n\n**Suggested fix:** in V8's `SourceTextModule::Evaluate` / `Module::Evaluate` path, when the module (or its cycle root) is *evaluating-async* and has no top-level capability yet, store a new capability on the cycle root and leave it pending until async evaluation fulfils, as the spec does. Do not resolve it right away. A regression test would be the five files above, expecting `ok 2 2`. The behaviour changed between V8 14.1 and 14.6. One candidate is the module-evaluation refactoring for `import defer`, but I have not bisected it.\n","author":{"url":"https://github.com/konard","@type":"Person","name":"konard"},"datePublished":"2026-10-17T12:11:43Z","url":"https://github.com/nodejs/node/issues/66806"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:6b004bc8-7959-8360-ccf7-f7d765bc282a |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | A1A8:321644:A6085:E140D:6AED336D |
| html-safe-nonce | 730edfcbee69547dfc5f82b7e4ce9f51d476fc94c48be938402555009c8835f5 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBMUE4OjMyMTY0NDpBNjA4NTpFMTQwRDo2QUVEMzM2RCIsInZpc2l0b3JfaWQiOiI5MDAzNDI0MTM5MTk1NTI4MDQ1IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 5ceed8757e98fc62aeb6de23f0347e95f12d581741fdc05355bc10f0fc1f237b |
| hovercard-subject-tag | issue:5898193270 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/66806/issue_layout |
| twitter:image | https://opengraph.githubassets.com/9cd3c00132bcb9786ac5e1e94947f450ed95f5d5c9bdf88f4e2e07000351c05a/nodejs/node/issues/66806 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/9cd3c00132bcb9786ac5e1e94947f450ed95f5d5c9bdf88f4e2e07000351c05a/nodejs/node/issues/66806 |
| og:image:alt | Version v26.12.0 (V8 14.6.202.34-node.39). It works in v24.22.0 (V8 13.6) and v25.9.0 (V8 14.1.146.11). Deno 2.9.7 (V8 15.0.245.2) fails the same way, so the bug looks like it is in V8. Platform Li... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | konard |
| hostname | github.com |
| expected-hostname | github.com |
| None | e51d0b7b5ab34b1688c05b54c3ed17b8658b2fa9bca973a9c740683ccb12cf4a |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 59ebb7b901cc154ecc179a44f266fcf69f343b30 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width