fix(renderer): the infinite loop due to which the app ui stays frozen
What & why
Refs #5496 — fixes the freeze, but does not close the issue (remaining items listed below).
When localStorage is full, the desktop app becomes permanently unusable: UI frozen, renderer pegged at 100%+ CPU, RSS climbing past 5 GB. Restarting the app or the machine doesn't help, because the full store is on disk. The only escape today is manually deleting ~/Library/Application Support/Superset/Partitions/superset/Local Storage.
localStorageCollectionOptions re-serializes the whole collection on every mutation and rethrows QuotaExceededError. The library treats that throw as a failed transaction and rolls the optimistic write back → the live query re-emits → the effect that queued the write retries → repeat, ~40x a second, with zero user interaction.
The fix guards the storage boundary rather than one call site. On quota error: reclaim orphaned terminal snapshots, retry once, then return without throwing. No throw means no rollback, so the loop cannot form at any of the ~15 sites that write to these collections — not just usePlaceLocalWorktreesInSidebar.
It also reclaims orphaned terminal-buffer:* / terminal-dims:* keys — snapshots for terminals that no longer exist. Eviction leaves those behind by design (terminal-runtime-registry.ts) and nothing ever collected them; the reporter found 528 such keys, ~8.5 MB. Reclaim runs on the quota path rather than at startup, so a profile left wedged by a previous session recovers on next launch instead of needing Finder.
Follows existing conventions in this directory: withQuotaGuard is a sibling of withReadHeal, and the pure-policy/side-effect split mirrors selectRuntimesToEvict. No library patch needed — localStorageCollectionOptions already accepts an injectable storage.
Deliberately out of scope (from the issue's other defects):
- No cap on
terminal-buffer:*for terminals still open (issue suggestis, so reclaim skips them — with enough live terminals there's nothing tofree and writes fail permanently. Degraded rather than frozen, but still permanent. Happy to take this as an immediate follow-up. - Tombstone rows, uncapped
viewedFiles, base64initialFilesin `paner growth contributors, untouched. - Still one key per org (issue suggestion 3). Neutralized, not restructured — that's a persistence-format migration and I'd want a maintainer's preference first, since the
existing SQLite helper is built for Electric-synced collections rather t
User-facing notice on genuine exhaustion is behind QUOTA_NOTICE_MODE i— currentlysilent (console warning only, matching howterminal-runtime-registryalready handles this error).notifyandoffer-reclaim` are implemented alongside it, so switching is a one-word change if you'd prefer a
toast.
How I tested it
Reproduced on unmodified main first, then replayed the identical six:
| Before | After (same wedged profile) | |
|---|---|---|
| Renderer CPU | 115% | 2% |
| RSS | 1.4 GB | 136 MB |
| Quota errors / 40s | 1,599 | 0 |
| Main-thread eval RTT | timed out ×3 | p50 0.8 ms, no timeouts |
| Orphaned snapshot keys | 413 | 0 |
| Workspace rows persisted | 0 (all rolled back) | 12 |
Repro: local stack via setup.local.sh, throwaway repo imported as a project, 12 worktree workspaces, localStorage filled to the real ceiling, then let sidebar placement run. Measured over CDP against this workspace's matched renderer. Beforeeenshotand evenRuntime.enable` timed out. Killing every process andrelaunching cold re-wedged within a minute — confirming the "restart doesn't help" report.
One correction to the issue: the quota was ~49 MB on Electron 40, not ~10 MB. Different threshold, identical mechanism.
Tests: 27 new unit tests across the four new modules. The regression test was verified to fail against unguarded code (the rollback is asynchronous, so asserting synchronously passes even when broken). Mutation-tested six deliberate bskip reclaim, swallow non-quota errors, break warn dedupe, ignore mode,leak the live Set — each killed a test.
clearAllTerminalSnapshots has an explicit test asserting it never touches collection data.
Full desktop suite: 2293 pass. The 8 failures on my machine are pre-existing and environmental (runTeardown + getProcessEnvWithShellPath fail when $SHELL is nushell,
since getCommandShellArgs falls back to -lc) — verified identical wiling separately.
Checklist
- PR title follows conventional commits (
type(scope): subject) -
bun run lintandbun run typecheckpass (CI fails on lint warnings too) - "Allow edits from maintainers" is checked on fork PRs
Summary by cubic
Fixes the freeze caused by an infinite retry loop when localStorage hits quota. Writes now fail soft: we reclaim safe terminal snapshots, retry once, then drop the write so the UI stays responsive and data stays in memory for the session.
- Bug Fixes
- Wrapped all localStorage-backed collections with
withQuotaGuard: onQuotaExceededError, reclaim, retry once, then return (no throw → no rollback loop across writers). - Reclaims only orphaned terminal snapshot keys via
TERMINAL_BUFFER_KEY_PREFIX/TERMINAL_DIMS_KEY_PREFIX; parked and released-but-restorable terminals are preserved via the registry’sgetRegisteredTerminalIds, anddisposeclears preserved records. - Added
notifyQuotaExhaustedbehindQUOTA_NOTICE_MODE(silentby default); “offer-reclaim” shows a toast with “Free up space” that clears saved scrollback and reports cleared entries (entries, not terminals). - Terminal runtime now uses shared key prefixes; reclaim runs on the quota path so wedged profiles recover on next launch; tests cover guard behavior, reclaim selection, notices, and preservation/cleanup paths.
- Wrapped all localStorage-backed collections with
Summary by CodeRabbit
-
Bug Fixes
- Prevented app freezes when browser storage quota is hit by safely handling failed persistence writes and avoiding retry loops.
- Improved terminal state restore by keeping “parked” scrollback and dimensions available, and correctly reflecting which terminals have restorable data.
- Automatically reclaims obsolete/unreachable terminal snapshots to make room for new saved state.
-
User Experience
- Added clearer handling for storage-capacity issues: configurable warning toasts, message deduping, and an option to free saved terminal scrollback so changes can persist.