Skip to content

fix(desktop,web): contain renderer memory growth and recover from renderer OOM crashes - #5148

Open
t3dotgg wants to merge 1 commit into
mainfrom
t3code/renderer-oom-containment
Open

fix(desktop,web): contain renderer memory growth and recover from renderer OOM crashes#5148
t3dotgg wants to merge 1 commit into
mainfrom
t3code/renderer-oom-containment

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Jul 31, 2026

Copy link
Copy Markdown
Member

The desktop renderer OOMs at V8's heap ceiling (~3.7 GB) on long sessions and dies to a white screen (#1686, and the claudeissues mirror users keep hitting). The window frame survives, the renderer doesn't, and agents keep running invisibly behind a dead UI.

Investigation on current main found three renderer-side growth/recovery gaps:

  1. Live context-window.updated rows accumulate forever. Providers stream these continuously; consumers only ever read the latest value per turn (walking backwards). The server already collapses them in snapshots (dropStaleContextWindowActivities, perf(server): trim stale context-window rows and drop dead replay RPC #4791) but deliberately left live events untouched — so a long-running session still retains thousands of rows per thread client-side. The reducer now applies the same per-turn collapse on the live path, with identical malformed-row semantics (an unresolvable row never replaces a resolvable one, so the meter can always derive a value).
  2. The sidebar pins up to 10 fully hydrated live thread subscriptions. Each prewarmed row holds every message and activity of its thread, growing as agents work, for as long as the row is visible — the 5-minute idle TTL never fires. Dropped to 3. Cold opens still render instantly from the cached snapshot, then transition to live.
  3. render-process-gone only logged. The window now reloads on crashed/oom/abnormal-exit, bounded to 3 attempts per rolling minute so a boot-crash can't reload-loop. The backend is a separate process and unaffected, so a reload fully rehydrates — the white screen becomes a ~1s blip instead of data loss.

No user-visible behavior change outside crash recovery itself.

Companion to #5147 (server-side OOM containment). Long term, the real fix is moving tool.completed payloads out of the hot paths entirely.

Verification:

  • 2 new reducer tests (same-turn collapse incl. malformed rows and cross-turn retention; malformed update never displaces a resolvable row)
  • client-runtime suite 510 passed, web suite 1754 passed, desktop suite 406 passed
  • typecheck (client-runtime, web, desktop, mobile), lint, format clean

Built by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Changes live event reduction and desktop crash recovery paths; bounded reload limits boot-loop risk, but incorrect collapse rules could affect context-window display or thread activity history.

Overview
Targets renderer heap growth and dead UI after V8 OOM on long desktop sessions without changing normal UX except when the renderer actually crashes.

Client thread state: On live thread.activity-appended, resolvable context-window.updated rows for the same turn now collapse to the newest (matching server snapshot dropStaleContextWindowActivities). Malformed rows do not replace earlier resolvable ones. This cuts thousands of redundant activity rows per thread over long agent runs.

Web sidebar: SIDEBAR_THREAD_PREWARM_LIMIT drops from 10 → 3, reducing how many visible rows keep full live thread-detail subscriptions (messages + activities) in memory.

Desktop main window: render-process-gone for crashed / oom / abnormal-exit triggers a bounded reload (500ms delay, max 3 attempts per rolling 60s), so users get a brief rehydrate instead of a permanent white screen while the backend keeps running.

Tests cover context-window collapse semantics in threadReducer.

Reviewed by Cursor Bugbot for commit 2d46b95. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix renderer memory growth and recover from renderer OOM crashes in desktop and web

  • Adds auto-reload recovery to the desktop DesktopWindow renderer: on crashed, oom, or abnormal-exit, waits 500ms then reloads, up to 3 times per 60s window before giving up and logging only.
  • Reduces the sidebar thread prewarm limit from 10 to 3 in Sidebar.logic.ts to lower concurrent detail subscriptions and memory pressure.
  • Collapses redundant context-window.updated activities in threadReducer: when a valid (finite, non-negative usedTokens) update is appended, earlier valid updates for the same turn are dropped; malformed updates are preserved and do not displace valid history.
  • Behavioral Change: the desktop app will now silently reload up to 3 times on renderer failure before requiring a manual restart.

Macroscope summarized 2d46b95.

…derer crashes

Long sessions OOM the Electron renderer at V8's heap ceiling (~3.7GB),
leaving a dead white window while agents keep running invisibly.

Three containments, no user-visible behavior change:

- Collapse superseded context-window.updated activities in the live
  reducer path. Providers stream these continuously and consumers only
  read the latest value per turn, but the client retained every row —
  thousands per long thread. Mirrors the rule the server already
  applies to snapshots (dropStaleContextWindowActivities), including
  its malformed-row and per-turn revert semantics.
- Drop the sidebar thread-detail prewarm from 10 to 3. Each prewarmed
  row pins a fully hydrated, live-growing thread subscription for as
  long as it is visible; cold opens still render instantly from the
  cached snapshot.
- Reload the window on render-process-gone (crashed/oom/abnormal-exit),
  bounded to 3 attempts per rolling minute so a boot-crash cannot
  reload-loop. The backend is unaffected by renderer death, so a reload
  fully rehydrates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c18c5428-d699-4d03-8622-f13e197f3f7c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Jul 31, 2026
@macroscopeapp

macroscopeapp Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new crash recovery behavior (auto-reload on renderer OOM) and changes how thread activity data is deduplicated to prevent memory growth. While well-bounded and tested, these runtime behavior changes to failure handling and state management warrant human review.

You can customize Macroscope's approvability policy. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant