Next.js Change
Commit: 4a95af8
PR: #97476
What changed
A fallback-shell 'use cache' prerender can combine its dynamic-access and timeout signals with AbortSignal.any(). React attaches an abort listener to that composite during prerender(). After a successful render, clearing the timeout alone leaves the composite signal non-empty and retains React's listener (and the successful render) in Node.
Next.js now snapshots whether the timeout had already fired, then aborts the existing timeout controller after clearTimeout() when the composite signal was used. That abort releases React's listener. The snapshot preserves real timeout handling: cleanup-triggered aborts must not be treated as timeouts. Cache prerenders without a dynamic-access signal continue to use the direct timeout signal unchanged.
Impact on vinext
vinext's Cache Components / 'use cache' prerender path must release composite abort signals after a successful fallback-shell render. Otherwise long-lived Node dev/build processes can retain successful React prerenders and leak memory.
This complements, rather than duplicates, #2812: that issue prevents a cache fill that starts after the outer prerender was aborted from persisting an empty stream. This upstream change retains that guard and fixes listener cleanup for successful cache prerenders.
Acceptance criteria
Related
Next.js Change
Commit:
4a95af8PR: #97476
What changed
A fallback-shell
'use cache'prerender can combine its dynamic-access and timeout signals withAbortSignal.any(). React attaches an abort listener to that composite duringprerender(). After a successful render, clearing the timeout alone leaves the composite signal non-empty and retains React's listener (and the successful render) in Node.Next.js now snapshots whether the timeout had already fired, then aborts the existing timeout controller after
clearTimeout()when the composite signal was used. That abort releases React's listener. The snapshot preserves real timeout handling: cleanup-triggered aborts must not be treated as timeouts. Cache prerenders without a dynamic-access signal continue to use the direct timeout signal unchanged.Impact on vinext
vinext's Cache Components /
'use cache'prerender path must release composite abort signals after a successful fallback-shell render. Otherwise long-lived Node dev/build processes can retain successful React prerenders and leak memory.This complements, rather than duplicates, #2812: that issue prevents a cache fill that starts after the outer prerender was aborted from persisting an empty stream. This upstream change retains that guard and fixes listener cleanup for successful cache prerenders.
Acceptance criteria
'use cache'called after prerender aborts must error, not fill an empty entry (cache-poisoning fix) #2812's after-prerender-abort guard so an empty cache entry is never persisted.Related
'use cache'called after prerender aborts must error, not fill an empty entry (cache-poisoning fix) #2812 — Cache fill after prerender abort must not poison entriesuse cachewrapper never releases its AbortSignal.any composite, retaining every cached render vercel/next.js#97363 — upstream memory-retention report