Skip to content

fix(dev): keep lazy handlers code-split so import-time errors surface correctly (v2) - #4458

Open
hiranuma wants to merge 1 commit into
nitrojs:v2from
hiranuma:fix/dev-inline-dynamic-imports-tdz
Open

fix(dev): keep lazy handlers code-split so import-time errors surface correctly (v2)#4458
hiranuma wants to merge 1 commit into
nitrojs:v2from
hiranuma:fix/dev-inline-dynamic-imports-tdz

Conversation

@hiranuma

Copy link
Copy Markdown

🔗 Linked issue

Fixes #4435 (for v2).

📚 Description

In dev, the nitro-dev preset sets Rollup inlineDynamicImports: true, which inlines every
lazy server handler into the single .nitro/dev/index.mjs and evaluates each handler module
eagerly at that bundle's top level. When one server module throws at import
(evaluation) time
, evaluation aborts after server.listen() has already run, leaving
unrelated later consts (e.g. renderer) permanently in the temporal dead zone. Every
request then fails with a misleading Cannot access '<x>' before initialization, while the
real error is only logged once at startup as an uncaughtException.

This has caused a long tail of reports that were closed as "circular dependency in user
code": #1670, nuxt/nuxt#20576, sidebase/nuxt-auth#271.

Fix: set inlineDynamicImports: false for the nitro-dev preset so lazy handlers stay
genuinely code-split in dev. An import-time failure is then isolated to its own route and
surfaces the real error instead of poisoning unrelated ones.

Behaviour, before → after (minimal repro: a healthy /ok route + a /boom route that throws
at import time):

request before after
GET /ok (unrelated) 500 Cannot access 'ok_get$1' before initialization 200 {"ok":true}
GET /boom 500 Cannot access 'ok_get$1' before initialization 500 with the real error at routes/boom.get.ts

🧪 Verification / scope note

  • Adds a regression test (test/unit/dev-import-error.test.ts + fixture) that fails before /
    passes after
    this change.
  • Full suite unchanged: the existing suite stays at 641 passed, 0 failures (same as
    baseline — zero regressions); with the added test the branch runs 643 / 0.
  • I verified the // externals plugin limitation concern doesn't reproduce in the current
    suite with code-splitting on, but I'd welcome a maintainer's read on whether there are
    externals scenarios the fixture doesn't cover.

Why this targets v2 and not main: the misleading-TDZ behaviour is v2-specific. I
verified on main (v3) that the bug does not reproduce — with the default rolldown dev
builder an import-time throw is already isolated (/ok → 200) and reports the real error, and
with the rollup builder the dev worker fails fast with the real error (503), never the TDZ. On
v3 this same inlineDynamicImports: false change is a no-op for rolldown and actually breaks
the rollup build (Cannot read properties of null (reading 'getVariableForExportName')). So
the fix belongs on v2, where users on current Nuxt (@nuxt/nitro-servernitropack@^2)
actually hit it. (Context on the parallel v3 PR: #4436.)

…rrectly

In dev, the nitro-dev preset set Rollup's `inlineDynamicImports: true`,
inlining every lazy server handler into a single `index.mjs` and evaluating
each module eagerly at that bundle's top level. When one server module throws
at import time, the whole bundle's evaluation aborts after the HTTP server has
already started listening, leaving unrelated `const`s (e.g. `renderer`) in
the temporal dead zone. Every request then fails with a misleading
`Cannot access '<x>' before initialization` while the real error is only
logged once as an uncaughtException.

Keeping lazy handlers as real, code-split dynamic imports isolates an
import-time failure to its own route and reports the real error.

Refs: nitrojs#1670, nuxt/nuxt#20576, sidebase/nuxt-auth#271
@hiranuma
hiranuma requested a review from pi0 as a code owner July 22, 2026 05:00
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Nitro Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8f95646-d8f7-4614-9b7d-8cb6a09c2660

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant