nextjs - ✅(Solved) Fix Turbopack miscompilation leading to runtime crash when project contains async modules, import cycles, and an unlucky import-graph shape [3 pull requests, 3 comments, 4 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#85988Fetched 2026-04-08 02:13:09
View on GitHub
Comments
3
Participants
4
Timeline
15
Reactions
4
Timeline (top)
commented ×3cross-referenced ×3labeled ×3closed ×1

Error Message

If a project contains an import cycle, has an import path from the cycle to an async module, and has a certain import-graph topology, then some function calls will fail at runtime with an error like:

Root Cause

If a project contains an import cycle, has an import path from the cycle to an async module, and has a certain import-graph topology, then some function calls will fail at runtime with an error like:

(0 , w.exampleFunction) is not a function

This will always be a function call from a file which does not participate in the import cycle, which has imported a function that is in a file which is in the import cycle. This happens because compute_async_module_info_single marked the imported file as an async module, but didn't propagated this to the file that imports it.

Fix Action

Fix / Workaround

I set up a local turbopack build and patched compute_async_module_info_single to do a second pass of graph.traverse_edges_from_entries_dfs, confirmed that this was marking additional modules as async, and confirmed that it fixed the issue in the places we were seeing it.

PR fix notes

PR #11669: Workaround turbopack async-import bug

Description (problem / solution / changelog)

Due to this bug, we're currently forcing Webpack rather than Turbopack for our builds. However when cacheComponents is enabled, Sentry crashes if webpack is selected rather than turbopack (see: https://github.com/getsentry/sentry-javascript/issues/17895 ; note that Sentry also crashes under Turbopack, but only if using a particular feature that we aren't).

As a workaround, change how certain libraries are imported so that they no longer propagate async-module-ness through the codebase. The imports to modify were identified by using a locally-patched version of turbopack, with debug logging added when for when module async-ness propagates.

This should allow us to modify our vercel-side config to no longer pass --webpack to yarn build, which will then allow us to merge the stuff that depends on cacheComponents.

┆Issue is synchronized with this Asana task by Unito

Changed files

  • .eslintrc.js (modified, +18/-21)
  • packages/lesswrong/integrationTests/testPreSetup.ts (modified, +1/-0)
  • packages/lesswrong/lib/domParser.ts (modified, +2/-1)
  • packages/lesswrong/lib/utils/createSingleton.ts (added, +12/-0)
  • packages/lesswrong/server/embeddings.ts (modified, +9/-7)
  • packages/lesswrong/server/languageModels/autoTagCallbacks.ts (modified, +1/-0)
  • packages/lesswrong/server/languageModels/criticismTipsBot.ts (modified, +3/-1)
  • packages/lesswrong/server/languageModels/languageModelIntegration.ts (modified, +1/-0)
  • packages/lesswrong/server/languageModels/modGPT.ts (modified, +3/-1)
  • packages/lesswrong/server/languageModels/promptUtils.ts (modified, +1/-11)
  • packages/lesswrong/server/maybeMinifyCSS.ts (modified, +1/-1)
  • packages/lesswrong/server/resolvers/aiArtResolvers/coverImageMutations.ts (modified, +1/-1)
  • packages/lesswrong/server/resolvers/anthropicResolvers.ts (modified, +15/-2)
  • packages/lesswrong/server/scripts/generativeModels/coverImage-2022Review.ts (modified, +1/-0)
  • packages/lesswrong/server/scripts/generativeModels/coverImages-2023Review.ts (modified, +8/-6)
  • packages/lesswrong/server/utils/wrapLinkedom.ts (modified, +4/-3)
  • packages/lesswrong/stubs/server/utils/wrapLinkedom.ts (modified, +5/-1)
  • packages/lesswrong/unitTests/testSetup.ts (modified, +1/-0)

PR #86391: Turboopack: fix async module cycle

Description (problem / solution / changelog)

Closes https://github.com/vercel/next.js/issues/85988 Closes PACK-5806

The previous approach didn't entirely work: the whole cycle was marked as async, but that wasn't then further propagated to importers of modules in the cycle.

Changed files

  • turbopack/crates/turbopack-core/src/module_graph/async_module_info.rs (modified, +35/-13)
  • turbopack/crates/turbopack-core/src/module_graph/export_usage.rs (modified, +1/-1)
  • turbopack/crates/turbopack-core/src/module_graph/mod.rs (modified, +3/-3)
  • turbopack/crates/turbopack-core/src/module_graph/module_batches.rs (modified, +1/-3)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/A.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/B.ts (added, +9/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/C.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/D.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/E.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/asyncImportFn.js (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/index.js (added, +27/-0)

PR #86427: [turbopack] Support traversing the graph in reverse order

Description (problem / solution / changelog)

This makes certain aggregations trivial since we are are guaranteed to only traverse relevant edges.

Use it to fix a bug in the async module identification logic. Previously we would aggregate async cycles after propagating 'asyncness' through the DFS post order traversal, but this could cause us to fail to propagate async to all reverse dependencies depending on which part of a cycle a node would happen to visit first.

By traversing starting from the async modules we just need to mark everything we find and not worry about cycles at all since the underlying DFS mechanism will ensure we don't loop. In addition to only requiring a single pass, this is guaranteed to visit fewer nodes in that single pass.

One caveat is that reverse traversals only work within a single SingleModuleGraph so a debug assert was added to prevent misuse

Closes #85988 Closes #86391 Closes PACK-5806

Changed files

  • contributing/core/testing.md (modified, +13/-0)
  • turbopack/crates/turbopack-core/src/module_graph/async_module_info.rs (modified, +41/-40)
  • turbopack/crates/turbopack-core/src/module_graph/mod.rs (modified, +265/-25)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/A.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/B.ts (added, +9/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/C.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/D.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/E.ts (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/asyncImportFn.js (added, +7/-0)
  • turbopack/crates/turbopack-tests/tests/execution/turbopack/async-modules/cycle-2/input/index.js (added, +27/-0)

Code Example

(0 , w.exampleFunction) is not a function

---

api/test/route.ts
 |   \
 v    v
 A<-  D
 |  \ |
 v   \v
 B--->C
 |
 v
 async

---

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:49 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6000
  Available memory (MB): 65536
  Available CPU cores: 10
Binaries:
  Node: 22.21.1
  npm: 10.9.4
  Yarn: N/A
  pnpm: 9.6.0
Relevant Packages:
  next: 16.0.2-canary.13
  eslint-config-next: N/A
  react: 19.3.0-canary-fa50caf5-20251107
  react-dom: 19.3.0-canary-fa50caf5-20251107
  typescript: 5.9.2
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/jimrandomh/turbopack-issue-repro

To Reproduce

Start a server using the linked reduced repro and yarn dev or yarn build && yarn start, and make a request to http://localhost:3000/api/test.

Current vs. Expected behavior

If a project contains an import cycle, has an import path from the cycle to an async module, and has a certain import-graph topology, then some function calls will fail at runtime with an error like:

(0 , w.exampleFunction) is not a function

This will always be a function call from a file which does not participate in the import cycle, which has imported a function that is in a file which is in the import cycle. This happens because compute_async_module_info_single marked the imported file as an async module, but didn't propagated this to the file that imports it.

compute_async_module_info_single determines which modules are async by (1) propagating async-ness backwards across import relations with a depth-first traversal, then (2) propagating async-ness through import cycles. This is incorrect in that it stops too early; after propagating async-ness through an import cycle, it needs to return to (1), since there may be files that imported something in the cycle that weren't marked.

(Repeating-until-no-changes is definitely correct, here, but might be noticeably slow. There is a fancy graph-alpgorithm solution to this that would be faster, I think.)

In the linked test case, the import graph has the following topology:

 api/test/route.ts
 |   \
 v    v
 A<-  D
 |  \ |
 v   \v
 B--->C
 |
 v
 async

In this test case, D is not marked as an async module, and the function call from D to C fails.

This impacted our project (ForumMagnum aka LessWrong/EA Forum) despite us not intentionally using any async modules, because some libraries, if listed in serverExternalPackages in NextConfig, are treated as async modules even if there's nothing async inside them (presumably because turbopack is making conservative assumptions without looking inside the library).

I set up a local turbopack build and patched compute_async_module_info_single to do a second pass of graph.traverse_edges_from_entries_dfs, confirmed that this was marking additional modules as async, and confirmed that it fixed the issue in the places we were seeing it.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:49 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6000
  Available memory (MB): 65536
  Available CPU cores: 10
Binaries:
  Node: 22.21.1
  npm: 10.9.4
  Yarn: N/A
  pnpm: 9.6.0
Relevant Packages:
  next: 16.0.2-canary.13
  eslint-config-next: N/A
  react: 19.3.0-canary-fa50caf5-20251107
  react-dom: 19.3.0-canary-fa50caf5-20251107
  typescript: 5.9.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Turbopack

Which stage(s) are affected? (Select all that apply)

next build (local)

Additional context

No response

extent analysis

TL;DR

The most likely fix is to modify the compute_async_module_info_single function to perform an additional pass of graph.traverse_edges_from_entries_dfs to correctly propagate async-ness through import cycles.

Guidance

  • The issue is caused by the compute_async_module_info_single function not correctly propagating async-ness through import cycles, resulting in some modules not being marked as async.
  • To verify the issue, start a server using the provided repro and make a request to http://localhost:3000/api/test, which should result in a runtime error.
  • A potential workaround is to patch the compute_async_module_info_single function to perform an additional pass of graph.traverse_edges_from_entries_dfs to correctly mark all async modules.
  • The import graph topology and the use of async modules in libraries listed in serverExternalPackages in NextConfig can contribute to this issue.

Example

No code snippet is provided as the issue is related to a specific function in the turbopack library, and modifying it requires a deeper understanding of the library's internals.

Notes

The provided test case and environment information suggest that this issue is specific to the turbopack library and the Next.js framework. The fix may require modifications to the turbopack library or updates to the Next.js framework.

Recommendation

Apply a workaround by patching the compute_async_module_info_single function to perform an additional pass of graph.traverse_edges_from_entries_dfs, as this has been confirmed to fix the issue in the provided test case.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - ✅(Solved) Fix Turbopack miscompilation leading to runtime crash when project contains async modules, import cycles, and an unlucky import-graph shape [3 pull requests, 3 comments, 4 participants]