nextjs - ✅(Solved) Fix Segment cache LRU cleanup causes infinite loop and page freeze when cache exceeds size limit [1 pull requests, 2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#88512Fetched 2026-04-08 02:04:38
View on GitHub
Comments
2
Participants
3
Timeline
11
Reactions
0
Timeline (top)
labeled ×3commented ×2subscribed ×2closed ×1

Fix Action

Fixed

PR fix notes

PR #88586: Fix/88512 cache LRU infinite loop

Description (problem / solution / changelog)

Fixing a bug

What?

This PR fixes incorrect LRU size accounting when inserting cache entries.

  • setInCacheMap was updating the LRU size redundantly, causing entries to be double-counted. This could lead to cleanup() looping indefinitely because the LRU size would not decrease as entries were evicted.
  • The while loop, as mentioned in #88512 was running infinitely leading to page freeze as LRU size was not being reduced after deleting the map entities. Added a condition to break out of it to avoid the infinite loop.

Why?

The LRU implementation relies on the invariant that each entry contributes its size exactly once when it enters the LRU.
However, setInCacheMap was calling updateLruSize in addition to the size update performed during value assignment and LRU insertion. This broke the invariant and caused the total LRU size to become inflated.

As a result, the cleanup loop could fail to make progress and run indefinitely.


How?

The fix removes the redundant updateLruSize call from setInCacheMap.

  • setMapEntryValue remains the single source of truth for assigning an entry’s size
  • lruPut remains responsible for adding a new entry’s size to the global LRU total
  • This restores correct size accounting and ensures cleanup terminates as expected
  • Adds a protective condition to avoid the infinite loop and page freeze

Closes NEXT- Fixes #88512

Changed files

  • packages/next/src/client/components/segment-cache/cache-map.ts (modified, +0/-1)
  • packages/next/src/client/components/segment-cache/lru.ts (modified, +7/-0)

Code Example

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.2.0: Tue Nov 18 21:07:05 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6020
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 22.19.0
  npm: 10.9.3
  Yarn: 4.3.1
  pnpm: 10.18.1
Relevant Packages:
  next: 16.1.1-canary.25 // Latest available version is detected (16.1.1-canary.25).
  eslint-config-next: N/A
  react: 19.2.3
  react-dom: 19.2.3
  typescript: 5.9.3
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/yoshiwarab/nextjs-lru-bug-repro

To Reproduce

  1. Clone the reproduction repository
  2. Run npm install
  3. Run npm run build && npm run start
  4. Open http://localhost:3000 in the browser
  5. Scroll up and down through the grid of collection links, hovering over various items to trigger prefetches (each prefetch loads ~1MB of static RSC payload)
  6. Continue scrolling and hovering until the page freezes (typically after 50+ prefetches exceed the 50MB cache limit)

Note: Simply scrolling slowly once may not be enough to trigger the issue. Scrolling back and forth and hovering over links helps trigger more prefetch activity, which fills the cache faster.

Current vs. Expected behavior

Current behavior: When the segment cache exceeds the 50MB limit, the cleanup() function in segment-cache/lru.js enters an infinite loop. The while loop condition lruSize > ninetyPercentMax && head !== null never becomes false, causing the page to freeze indefinitely. The main thread becomes completely blocked and the page is unresponsive.

Expected behavior: The LRU cache should evict old entries and reduce lruSize appropriately, allowing the cleanup to complete and the page to remain responsive.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.2.0: Tue Nov 18 21:07:05 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6020
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 22.19.0
  npm: 10.9.3
  Yarn: 4.3.1
  pnpm: 10.18.1
Relevant Packages:
  next: 16.1.1-canary.25 // Latest available version is detected (16.1.1-canary.25).
  eslint-config-next: N/A
  react: 19.2.3
  react-dom: 19.2.3
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Linking and Navigating, Performance

Which stage(s) are affected? (Select all that apply)

Vercel (Deployed), next start (local)

Additional context

Live demo: https://nextjs-lru-bug-repro.vercel.app/ The issue occurs in the segment cache's LRU eviction logic at https://github.com/vercel/next.js/blob/e1c95fc2094f276eb881539d45e0f6f8e42a83e9/packages/next/src/client/components/segment-cache/lru.ts#L104-L120

The while loop assumes that deleteMapEntry(tail) will always decrease lruSize, but under certain conditions this doesn't happen, causing an infinite loop.

The reproduction uses 100 statically generated pages, each with ~1MB of high-entropy data. When these pages are prefetched and the cache fills beyond 50MB, the cleanup triggers and freezes the page. This is reproducible locally with the production build (next build && next start). Prefetching must be enabled (production mode only).

extent analysis

TL;DR

The LRU cache cleanup function enters an infinite loop when the cache exceeds the 50MB limit, causing the page to freeze, and this can be addressed by modifying the cleanup logic to handle cases where deleteMapEntry(tail) does not decrease lruSize.

Guidance

  • Review the LRU eviction logic in segment-cache/lru.ts to identify why deleteMapEntry(tail) does not always decrease lruSize, potentially due to edge cases or incorrect assumptions about the cache state.
  • Consider adding a safeguard to the while loop condition to prevent infinite loops, such as a maximum iteration count or a check for specific conditions that indicate the cache is in an inconsistent state.
  • Investigate whether the issue is related to the high-entropy data used in the reproduction, and whether using different data or adjusting the cache limits could mitigate the problem.
  • To verify the fix, run the reproduction steps and monitor the page's responsiveness and cache size to ensure that the cleanup function completes successfully and the page remains interactive.

Example

// Pseudocode example of adding a safeguard to the while loop
while (lruSize > ninetyPercentMax && head !== null && iterationCount < MAX_ITERATIONS) {
  // ...
  iterationCount++;
}

Notes

The provided reproduction steps and environment information suggest that this issue is specific to the Next.js version and configuration used, and may not be representative of all possible use cases. Additionally, the fix may require a deeper understanding of the LRU cache implementation and its interactions with the rest of the Next.js codebase.

Recommendation

Apply a workaround by modifying the LRU eviction logic to handle edge cases and prevent infinite loops, as this is a more targeted and immediate solution than attempting to upgrade to a potentially non-existent fixed version.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING