next.js
c8c9fc6a - Fix dev mode reload and error on back navigation between Next.js pages (#93486)

Commit
131 days ago
Fix dev mode reload and error on back navigation between Next.js pages (#93486) Follow-up to #92892, addressing the regression reported in https://github.com/vercel/next.js/issues/93136#issuecomment-4298124826. That PR persisted the dev-mode debug channel chunks under a single shared `sessionStorage` key (`__next_debug_channel`) so the data could be replayed when a page is served from HTTP cache. The single-key design holds when the only navigations away from a Next.js page go to other origins, but it breaks the moment two Next.js documents share the same tab. When a user does an MPA navigation from page A to page B (a regular `<a href>` link to another Next.js route), B's persisted entry overwrites A's under the same key. Clicking back restores A from HTTP cache; A's bootstrap re-executes with its original `self.__next_r`, looks up `sessionStorage`, finds B's `requestId`, returns `undefined` from `restoreDebugChannelFromSessionStorage`, and falls back to `location.reload()`. Before the reload completes, the main RSC stream closes; the Flight client surfaces references to debug-channel chunks that block other model chunks — references the debug channel was supposed to deliver but never did — as a `Connection closed.` error, briefly flashing the error UI. The fix changes the storage key from `__next_debug_channel` to a per-document prefix `__next_debug_channel:${requestId}`. Each initial document persists into its own entry, so A's data survives B's load and the back-restore finds it. `requestId` is dropped from the stored value since it's now part of the key. On `setItem` failure (likely a quota issue), other entries with the prefix are removed and `setItem` is retried once; if it still fails, persistence is silently skipped and `location.reload()` remains the safety net on the read side. The fallback path also gets a small change: when restore legitimately fails (e.g., session storage cleared mid-session), `createDebugChannel` now returns `{ readable: new ReadableStream() }` rather than `{ readable: undefined }`. The empty stream never enqueues and never closes, so from the Flight client's perspective the debug channel is still active when the main stream closes — the unresolved chunk references no longer surface as a synchronous error before `location.reload()` tears the document down. The `bfcache-regression` test was updated to reproduce the bug. It now clicks an internal MPA link to `/target-page` instead of an external one, and asserts no console errors after the round trip via `assertNoConsoleErrors`. The fixture pre-warms `/target-page` compilation in parallel with the browser load in webpack mode, to avoid an unrelated Fast Refresh full-reload warning when the route is compiled on demand.
Author
Parents
Loading