Skip to content

Fix stalled RSC poisoning BFCache and dropping later clicks - #6

Closed
QuarkOS wants to merge 2 commits into
canaryfrom
cursor/fix-poisoned-bfcache-stale-nav-23ea
Closed

QuarkOS wants to merge 2 commits into
canaryfrom
cursor/fix-poisoned-bfcache-stale-nav-23ea

Conversation

@QuarkOS

@QuarkOS QuarkOS commented Aug 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

A stalled dynamic RSC request could permanently kill soft navigation to that route. With experimental.staleTimes.dynamic > 0, later clicks issued zero requests until staleAt expired, because writeToBFCache stored a never-settling rsc thenable and readFromBFCacheDuringRegularNavigation served it with needsDynamicRequest: false.

This change:

  • Reuses a BFCache entry on regular navigation only when the dynamic RSC (and head, if present) has actually settled with renderable data
  • Stops adopting in-flight prefetch thenables as prefetchRsc (React's useDeferredValue parks on that thenable; a hung prefetch then blocked later successful dynamic responses)

Fixes vercel#98066

Related: vercel#97807, vercel#97036, vercel#96233. vercel#97815 only adds an MPA fallback when navigateToUnknownRoute rejects; it does not evict a poisoned BFCache entry, so this is not a second closer for the same bug.

Upstream PR (into vercel/next.js): vercel#98087

Verification

Command:

HEADLESS=true pnpm test-start-turbo test/e2e/app-dir/segment-cache/poisoned-bfcache-stale-nav/poisoned-bfcache-stale-nav.test.ts

Failing without the render-tree.ts change (canary client, same test):

FAIL Turbopack test/e2e/app-dir/segment-cache/poisoned-bfcache-stale-nav/poisoned-bfcache-stale-nav.test.ts
  ● segment cache (poisoned BFCache / staleTimes.dynamic) › does not drop a later click after a stalled RSC navigation (issue 98066)

    Timed out waiting for a request to be initiated.
      at Object.act (... poisoned-bfcache-stale-nav.test.ts:85:11)

Test Suites: 1 failed, 1 total

The later click from /other issued zero RSC requests — the poisoned BFCache entry was served with needsDynamicRequest: false.

Passing with the fix:

PASS Turbopack test/e2e/app-dir/segment-cache/poisoned-bfcache-stale-nav/poisoned-bfcache-stale-nav.test.ts
  segment cache (poisoned BFCache / staleTimes.dynamic)
    ✓ does not drop a later click after a stalled RSC navigation (issue 98066)

Test Suites: 1 passed, 1 total
Open in Web Open in Cursor 

A navigation whose dynamic RSC never settles wrote a pending thenable
into the BFCache. With staleTimes.dynamic > 0, later clicks reused that
entry with needsDynamicRequest: false, so the route stayed dead until
staleAt with zero network. Skip unsettled BFCache entries on regular
navigations and do not adopt in-flight prefetch thenables as prefetchRsc.

Fixes vercel#98066
Assert the later click from a fresh page after the network is restored,
so a poisoned BFCache entry is the thing under test. Without the fix
this click issues zero requests; with it the navigation commits.

QuarkOS commented Sep 28, 2026

Copy link
Copy Markdown
Owner Author

Closing this as part of a cleanup of my stale/slow-moving PRs. Thanks for your time!

@QuarkOS QuarkOS closed this Sep 28, 2026
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.

Segment cache: a stalled RSC request permanently kills soft navigation to a route (zero-request dropped clicks with staleTimes.dynamic > 0)

2 participants