cachedNavigations session-shell prerender races an async cacheHandlers.default get: E1163 on every dynamic render with deterministic keys
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.4k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://github.com/vicanera/next-cached-navigations-cache-handler-race
To Reproduce
- Next 16.3.4,
cacheComponents: true,partialPrefetching: true, and acacheHandlers.defaultwhosegetawaits I/O. In the linked reposlow-cache-handler.jswraps Next's own default in-memory handler and adds a few milliseconds of latency togetat runtime, standing in for a Redis/Memcached handler. - A page whose cached reads arrive in more than one batch (a second batch of
"use cache"reads starts only after the first batch has resolved), with a cookie-bound child behind<Suspense>so the route is a dynamic render with a static shell. next build && next start, then request the page repeatedly withNEXT_PRIVATE_DEBUG_CACHE=1.
In a production app with this configuration and a Redis-backed handler, roughly half of the renders of the affected page log three E1163 warnings (one per cached read in the second batch); routes whose reads all start in one batch never do. Fallback-param renders additionally log NEXT_STATIC_GEN_BAILOUT ("Next.js encountered uncached or runtime data during prerendering") from the same spawned prerender.
Honest status of the linked repo: the isolated shape there has not yet fired the warning locally (0/30 at 5 ms and 20 ms handler latency). The debug trail below is from the production app on the same Next version; the exact batching that opens the window has not been reduced further. Included because the source-level mechanism is deterministic and the workaround is verified.
Current vs. Expected behavior
Current. With experimental.cachedNavigations (auto-enabled under cacheComponents) and Partial Prefetching on, every dynamic render (renderToStream for documents, generateStagedDynamicFlightRenderResultNode for RSC navigations) installs a CacheSignal and, on cacheSignal.cacheReady(), spawns spawnRuntimePrefetchWithFilledCaches (mode rewindable-session-shell). cacheReady() is a quiescence heuristic (cache-signal.js: read count 0 → setImmediate → setTimeout(0) with no new beginRead). In use-cache-wrapper.js, a cache-handler hit is written into the resume data cache only after await cacheHandler.get() (the "leader resolved with cache handler hit" path), whereas a generated entry is registered before the function runs (saveToResumeDataCache). So when a render's reads come in two batches, the count touches zero between them; with an async handler the spawned prerender then looks keys up while the second batch's gets are still pending, misses, warns:
Error: Unexpected cache miss after cache warming phase during prerendering. This is likely caused by non-deterministic arguments ... (E1163)
and demotes the segment to a runtime hole. Every miss shows the same ordering for the affected key (NEXT_PRIVATE_DEBUG_CACHE=1):
use-cache: Resume Data Cache entry not found [key] <- the render's own lookup
use-cache: registering as intra-request invocation leader [key]
use-cache: registering as cross-request invocation leader [key]
use-cache: Resume Data Cache entry not found [key] <- the spawned prerender's lookup
Error: Unexpected cache miss after cache warming phase during prerendering ...
use-cache: leader resolved with cache handler hit [key] <- the handler get resolves last
A request that does not miss shows leader resolved with cache handler hit before the second lookup. The arguments are deterministic; the message's diagnosis (non-deterministic arguments) does not apply. With the default in-memory handler get resolves within the same tick, so the race never shows there.
Expected. No warning and no demotion when cache keys are deterministic and the handler simply takes a few milliseconds. Either the pending handler read should hold the CacheSignal until the entry is registered in the resume data cache, or a handler hit should be registered in the resume data cache as a pending entry before the get is awaited, as the generated-entry path already does.
Workaround. experimental: { cachedNavigations: false } → 0 warnings over 85 requests in the production app; prefetch-driven navigations (next-router-prefetch 2/3, which take generateRuntimePrefetchResult) unaffected. The same code shape is present in 16.4.0-canary.26.
Provide environment information
Operating System:
Platform: darwin (production: linux x64 container)
Arch: arm64
Binaries:
Node: 26.2.0
npm: 11.13.0
Relevant Packages:
next: 16.3.4
react: 19.2.0
react-dom: 19.2.0
Next.js Config:
cacheComponents: true, partialPrefetching: true, cacheHandlers.default: custom (async get)
Which area(s) are affected? (Select all that apply)
Cache Components, Partial Prefetching / Runtime Prefetching, Use Cache
Which stage(s) are affected? (Select all that apply)
next start (local), Other (deployed to a self-hosted container)
Additional context
Also reproduced with a real Redis-backed cacheHandlers.default pointed at a throwaway redis:7-alpine: 45 warnings over 30 requests of the affected page on the merged build, 0 over 85 with cachedNavigations: false. Happy to add the debug trail of a failing request in full if useful.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked reproduction, then read cache-signal.js and use-cache-wrapper.js around cacheReady(), handler get(), and resume-data registration. Trace renderToStream and generateStagedDynamicFlightRenderResultNode with an async cache handler and two read batches. Done means deterministic keys produce no E1163 warnings or runtime-hole demotions without disabling cachedNavigations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, next.js, react, redis
- Domain
- backend, performance, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100