Skip to content

fix(build): keep node:fs out of the client chunk graph (Turbopack /login + /dashboard) - #13568

Closed
jonlwheat2-gif wants to merge 1 commit into
cheaper-inference:release/v3.8.51from
jonlwheat2-gif:fix/turbopack-node-fs-client-chunk
Closed

jonlwheat2-gif wants to merge 1 commit into
cheaper-inference:release/v3.8.51from
jonlwheat2-gif:fix/turbopack-node-fs-client-chunk

Conversation

@jonlwheat2-gif

Copy link
Copy Markdown
Contributor

Summary

Turbopack (the Next 16 default bundler) was panicking on npm run dev while writing the page endpoints for /login and /(dashboard)/dashboard, so both routes returned 500:

FATAL: An unexpected Turbopack error occurred.
Failed to write app endpoint /login/page
Caused by:
- the chunking context (unknown) does not support external modules
  (request: node:fs)

This PR fixes the root cause. It is a build/bundling bug, unrelated to #13377.

Root cause

A client component drags a module with top-level Node builtins into the browser chunk graph. There is exactly one chain feeding both failing pages:

src/app/login/page.tsx            (and src/app/(dashboard)/layout.tsx)
 → src/shared/components/index.tsx               ← barrel
 → src/shared/components/ModelSelectModal.tsx    ← client component
 → src/shared/constants/models.ts
 → @omniroute/open-sse/config/providerModels.ts
 → @omniroute/open-sse/config/providerRegistry.ts
 → @omniroute/open-sse/config/providers/index.ts
 → .../providers/registry/codebuddy-cn/index.ts
 → src/lib/oauth/constants/oauth.ts
 → @omniroute/open-sse/utils/cursorAgentCliVersion.ts   ← top-level node:fs / node:os / node:path

fs / path / os / child_process are listed in serverExternalPackages (next.config.mjs), so Turbopack externalises them — and the browser chunking context cannot emit an external reference, hence the panic.

What it is NOT

Fix

Split the pinned build id into a client-safe module with no Node builtins:

File Change
open-sse/utils/cursorAgentCliVersion.constants.ts (new) Holds only CURSOR_AGENT_CLI_VERSION (+ a header comment documenting the chain above). No Node builtins.
open-sse/utils/cursorAgentCliVersion.ts Imports the constant from .constants.ts and re-exports it, so the module's public API is unchanged. Its fs/os/path machinery is untouched.
src/lib/oauth/constants/oauth.ts Imports CURSOR_AGENT_CLI_VERSION from the .constants.ts module instead of the fs-bearing one.

Note: all Node builtins had to leave that module's top level, not just fs — node:os and node:path would have panicked next.

Verification

  • /login → 200 (was 500)
  • /dashboard → 307/308 → /login (normal auth redirect)
  • /api/monitoring/health → {"status":"healthy","setupComplete":true}
  • Zero new next-panic-*.log files
  • npx tsc --pretty false -p tsconfig.typecheck-core.json → clean
  • tests/unit/cursor-agent-cli-version.test.ts + tests/unit/cursor-login-pkce.test.ts → 26/26

Escape hatch

OMNIROUTE_USE_TURBOPACK=0 npm run dev forces webpack, which tolerates the node:fs reference and lets the pages compile — but it is a workaround, not a fix.

…gin + /dashboard)

Turbopack (the Next 16 default bundler) panicked while writing the page
endpoints for /login and /(dashboard)/dashboard:

    FATAL: An unexpected Turbopack error occurred.
    Failed to write app endpoint /login/page
    Caused by:
    - the chunking context (unknown) does not support external modules
      (request: node:fs)

Root cause is a single import chain that drags a module with top-level Node
builtins into a CLIENT component's bundle:

    src/app/login/page.tsx   (and src/app/(dashboard)/layout.tsx)
     -> src/shared/components/index.tsx               (barrel)
     -> src/shared/components/ModelSelectModal.tsx    (client component)
     -> src/shared/constants/models.ts
     -> @omniroute/open-sse/config/providerModels.ts
     -> @omniroute/open-sse/config/providerRegistry.ts
     -> @omniroute/open-sse/config/providers/index.ts
     -> .../providers/registry/codebuddy-cn/index.ts
     -> src/lib/oauth/constants/oauth.ts
     -> @omniroute/open-sse/utils/cursorAgentCliVersion.ts
        (top-level node:fs / node:os / node:path)

fs/path/os/child_process are listed in `serverExternalPackages` in
next.config.mjs, so Turbopack externalises them — and the browser chunking
context cannot emit an external reference, hence the panic. This is NOT cache
corruption: purging the 1.1 GB `.build/next/dev/cache/turbopack` did not help,
and the same panic predates the auth work.

Fix: split the pinned build id into a client-safe module with no Node builtins.

- NEW  open-sse/utils/cursorAgentCliVersion.constants.ts
       holds only CURSOR_AGENT_CLI_VERSION (+ a header comment documenting the
       chain above), so it is safe to import from browser-bundleable code.
-      open-sse/utils/cursorAgentCliVersion.ts
       imports the constant from .constants.ts and re-exports it, so the
       module's public API is unchanged; its fs/os/path machinery is untouched.
-      src/lib/oauth/constants/oauth.ts
       imports CURSOR_AGENT_CLI_VERSION from the .constants.ts module instead
       of the fs-bearing one.

All Node builtins had to leave the module's top level, not just fs — node:os
and node:path would have panicked next.

Verified:
- /login -> 200 (was 500); /dashboard -> 307/308 -> /login (normal auth redirect)
- /api/monitoring/health -> {"status":"healthy","setupComplete":true}
- zero new next-panic-*.log files
- npx tsc --pretty false -p tsconfig.typecheck-core.json -> clean
- tests/unit/cursor-agent-cli-version.test.ts + tests/unit/cursor-login-pkce.test.ts -> 26/26

Escape hatch (workaround, not a fix): OMNIROUTE_USE_TURBOPACK=0 npm run dev
forces webpack, which tolerates the node:fs reference.
@jonlwheat2-gif

Copy link
Copy Markdown
Contributor Author

CI note — the failing checks are pre-existing, not from this PR.

Every failing job here fails identically on release/v3.8.51 and on the other open PRs against it (#13565, #13566, #13571). Passing on all of them: API Route Typecheck, Vitest (fast-path), semgrep.

Failing everywhere:

Docs Gates (fast-path) · Fast Quality Gates · Merge integrity (changelog + generated skills) · No new ESLint warnings · Unit Tests fast-path (1/4–4/4)

Two pinned from the logs:

  • Merge integrity → npm run check:agent-skills-sync exits 2 because the generator wants to create a cli-tunnel SKILL.md that isn't committed (Generated: 1 · Unchanged: 45 … + cli-tunnel).
  • Unit Tests → e.g. each rotated account attempt acquires and releases a fresh composite slot, repository contract is in sync (live data), Failed to configure Qwen Code: Expected property name or '}' in JSON — none of these touch this PR's files.

The base commit 152d95108 is itself red (Node 24/26 Compat, A11y axe, Validate main branch).

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks for tracking this down — the root cause you found (oauth.ts pulling node:fs via
cursorAgentCliVersion.ts) is confirmed real. There's another open PR (#13604) fixing the exact
same edge with an equivalent approach, plus a regression-guard test update your PR doesn't
include. To avoid landing two different "extract the pin to a sibling file" patterns for the
same bug, we're going with #13604 and closing this one — appreciate you finding and fixing it
independently, and the extra detail in your comments was useful cross-reference material.

Triage note: this is the review recommendation — the close itself happens only after the maintainer's per-PR sign-off (and, where a superseding PR is named, after it has landed). Nothing is being closed by this comment.

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks for the fix! The node:fs client-bundle break landed via #13436, which moves the same server-only imports out of the client chunk graph and also widens the client-bundle guard so any Node builtin reaching a client entry fails the test (with resolved-edge caching so the guard stays fast). With that merged this PR no longer applies cleanly and its change is covered. Closing as covered by #13436.

@jonlwheat2-gif
jonlwheat2-gif deleted the fix/turbopack-node-fs-client-chunk branch September 30, 2026 15:38
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.

2 participants