Skip to content

fix(renderer): the infinite loop due to which the app ui stays frozen - #5965

Closed
snehendu098 wants to merge 5 commits into
ParadevAI:mainfrom
snehendu098:fix/quotaexceederror-loop
Closed

snehendu098 wants to merge 5 commits into
ParadevAI:mainfrom
snehendu098:fix/quotaexceederror-loop

Conversation

@snehendu098

@snehendu098 snehendu098 commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

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, base64 initialFiles in `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 lint and bun run typecheck pass (CI fails on lint warnings too)
  • "Allow edits from maintainers" is checked on fork PRs

Open in Stage

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: on QuotaExceededError, 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’s getRegisteredTerminalIds, and dispose clears preserved records.
    • Added notifyQuotaExhausted behind QUOTA_NOTICE_MODE (silent by 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.

Written for commit ac46d41. Summary will update on new commits.

Review in cubic

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.

…nderer

`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, which makes the live
query re-emit and the queuing effect retry — a synchronous loop that pegs the
renderer and survives restarts, because the full store is on disk.

Guard the storage boundary instead of 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.

Reclaim runs on the quota path rather than at startup, so a profile left wedged
by a previous session recovers on next launch.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds shared terminal snapshot keys, parked-terminal registry tracking, orphaned snapshot reclamation, quota-safe localStorage persistence, quota notifications, collection integration, and tests covering these behaviors.

Changes

Terminal snapshot quota handling

Layer / File(s) Summary
Terminal identity and persistence keys
apps/desktop/src/shared/constants.ts, apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts, apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts, apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.test.ts
Shared buffer and dimension prefixes are used for terminal persistence, and the registry reports active and parked terminal IDs. Tests cover defensive copies, preserved snapshots, and full disposal.
Snapshot selection and reclamation
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/selectOrphanedTerminalSnapshots.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/terminalSnapshotStorage.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/*test.ts
Terminal snapshot keys are parsed, compared with reachable IDs, and selectively or fully removed while unrelated storage entries remain intact.
Quota-protected persistence
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.test.ts
Quota errors trigger snapshot reclamation and one retry when space is freed; persistent quota failures are reported without throwing, while non-quota errors are rethrown.
Notifications and collection integration
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.test.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
Quota notifications support silent, warning, and reclaim-action modes, and guarded persistence is applied to affected localStorage-backed collections.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Collection
  participant QuotaGuard
  participant SnapshotStorage
  participant TerminalRegistry
  participant LocalStorage
  Collection->>QuotaGuard: persist collection value
  QuotaGuard->>LocalStorage: attempt setItem
  QuotaGuard->>TerminalRegistry: getRegisteredTerminalIds
  QuotaGuard->>SnapshotStorage: reclaim orphaned snapshots
  SnapshotStorage->>LocalStorage: remove orphaned keys
  QuotaGuard->>LocalStorage: retry setItem
  QuotaGuard-->>Collection: complete write or report failure
Loading

Possibly related PRs

Suggested reviewers: avipeltz

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title follows conventional commit style and clearly summarizes the main fix to the renderer freeze loop.
Description check ✅ Passed The description covers what changed, why, testing, and the checklist sections required by the template.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@ghost

ghost commented Jul 26, 2026 •

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 5 individual chapters for you:

Title
1 Define terminal storage key constants
2 Track restorable terminal IDs in registry
3 Implement terminal snapshot reclamation logic
4 Create quota guard and notification utilities
5 Apply quota guard to all collections
Open in Stage

Chapters generated by Stage for commit ac46d41 on Jul 27, 2026 8:55pm UTC.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.test.ts (1)

53-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid wall-clock settling in this test.

setTimeout(resolve, 0) makes transaction settlement timing-dependent. Use the existing deterministic transaction-flush mechanism or an event-loop-friendly alternative that does not rely on real time. Based on learnings, test code should avoid real wall-clock timers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.test.ts`
around lines 53 - 55, Replace the wall-clock setTimeout-based implementation of
settleTransactions with the repository’s existing deterministic
transaction-flush mechanism or a non-time-based event-loop yielding alternative.
Keep settleTransactions responsible for waiting until mutation rollback
transactions have settled without relying on real timer timing.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts`:
- Around line 128-130: Update the terminal runtime registry around
getRegisteredTerminalIds, release(), eviction, and disposeEntry() to track
persisted snapshot reachability independently from live entryKeysByTerminalId.
Keep released or evicted terminals included for quota/orphan checks until
explicit dispose() removes their snapshot tracking, and add a regression test
covering release/eviction followed by the quota guard to ensure scrollback and
dimensions remain preserved.

In
`@apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.ts`:
- Around line 45-47: Update describeClearedSnapshots to label the count as
cleared terminal snapshots or entries, with accurate singular/plural wording
instead of terminals. In
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.ts
lines 45-47, change the message while preserving the zero-count text; update the
singular and plural assertions in
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.test.ts
lines 120-127 to match.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.test.ts`:
- Around line 53-55: Replace the wall-clock setTimeout-based implementation of
settleTransactions with the repository’s existing deterministic
transaction-flush mechanism or a non-time-based event-loop yielding alternative.
Keep settleTransactions responsible for waiting until mutation rollback
transactions have settled without relying on real timer timing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0294c9cf-8224-4959-b196-a015b54f4c3e

📥 Commits

Reviewing files that changed from the base of the PR and between 51a4d4b and 947b26b.

📒 Files selected for processing (13)
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.test.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/selectOrphanedTerminalSnapshots.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/selectOrphanedTerminalSnapshots.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/terminalSnapshotStorage.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/terminalSnapshotStorage.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.ts
  • apps/desktop/src/shared/constants.ts

Release and eviction persist a terminal's buffer and then drop its registry
entry, deliberately leaving the snapshot on disk for the next mount to restore.
Reclaim read that missing entry as "orphaned" and could delete the scrollback of
a pane the user still had open.

Track the terminals whose snapshot outlived their entry, mirroring what
disposeRuntime does with it: "preserve" keeps the snapshot, anything else has
already cleared it. dispose() clears the record, since that is what removes the
snapshot for real.

Also report cleared snapshots as storage entries rather than terminals — each
terminal persists a buffer and a dimensions key, so the old wording claimed
double the real count — and write the entry-key separator in tests as \u0000
instead of a literal NUL byte.
@snehendu098
snehendu098 force-pushed the fix/quotaexceederror-loop branch from 947b26b to d270573 Compare July 26, 2026 09:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.test.ts`:
- Around line 539-548: Update the test “stops reporting a terminal once dispose
clears its snapshot” to seed the terminal ID through the registry’s preserved
identity state, such as preservedSnapshotTerminalIds, rather than only
fakeStorage. Assert getRegisteredTerminalIds() contains terminalId before
calling dispose, then retain the existing assertion that it no longer contains
the ID afterward.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d6134950-33e7-4fc5-b5c1-eabc39382f48

📥 Commits

Reviewing files that changed from the base of the PR and between 947b26b and d270573.

📒 Files selected for processing (13)
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.test.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/selectOrphanedTerminalSnapshots.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/selectOrphanedTerminalSnapshots.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/terminalSnapshotStorage.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/terminalSnapshotStorage.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.ts
  • apps/desktop/src/shared/constants.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/selectOrphanedTerminalSnapshots.test.ts
  • apps/desktop/src/shared/constants.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/selectOrphanedTerminalSnapshots.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/terminalSnapshotStorage.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/notifyQuotaExhausted.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/terminalSnapshotStorage.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/withQuotaGuard.test.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/collections.ts

…ecord

The test seeded fakeStorage, but getRegisteredTerminalIds reads the registry
maps rather than storage — so the id was absent before dispose ran and the
assertion held either way. Seed the preserved record and assert it is reported
before disposal, so removing dispose's bookkeeping fails the test.
@snehendu098

Copy link
Copy Markdown
Contributor Author

Addressed the review feedback:

  • Preserved snapshots surviving release (d270573c4). This was a real bug. release() and eviction persist the buffer then drop the registry entry, so reclaim would have deleted the scrollback of a pane still open. Now tracks terminals whose snapshot outlived their entry, mirroring what disposeRuntime does with it; dispose() clears the record since that is what actually removes the snapshot. Regression test added on the real release() path, verified to fail without the fix.
  • Cleared-snapshot count (d270573c4). Was counting storage keys and reporting them as terminals, so it showed double. Now reads "N saved terminal entries".
  • Vacuous dispose test (b8cc31c97). The assertion held before dispose() ran, so it proved nothing. Now seeds the preserved record and asserts it is reported first; verified it fails when the bookkeeping is removed.

Two things I want to flag myself:

A gap in my own change. Reachability comes from registry state, which is empty after a restart. Panes mount lazily, so a quota error early in startup could treat a not-yet-mounted pane's snapshot as orphaned. Bounded (scroll history, not work, and the app was frozen outright before this) but unintended. The robust signal looks like the persisted pane layout: pane.data.terminalId where kind === "terminal" survives restarts and does not depend on mount timing. Happy to fold that in here, or keep it as a follow-up, whichever you prefer.

On the remaining issue item. Capping terminal-buffer:* for terminals that are still open is the one case left where a user can stay permanently stuck. Would you want that keyed off DEFAULT_TERMINAL_PARKED_RUNTIME_CAP, or a separate byte budget? Happy to take it either way.

@Kitenite

Copy link
Copy Markdown
Member

Thanks for this — the diagnosis of the rollback→retry loop was exactly right, and we're keeping your withQuotaGuard design and its tests nearly verbatim. This landed via #6038 instead of directly from this branch, for two reasons:

  1. It needed reworking on top of fix(desktop): garbage-collect persisted terminal scrollback in localStorage #6036, which merged first and made the persisted terminal snapshots self-bounding (boot GC + TTL + size budget, keyed by a durable terminal-buffer-persisted-at index). That index replaces this PR's orphan detection: the in-memory preservedSnapshotTerminalIds set doesn't survive a relaunch, so a quota event shortly after boot could have reclaimed restorable snapshots of terminals in workspaces the user hadn't opened yet. Driving reclaim off the persisted index (24h pressure TTL) fixes that and let us drop selectOrphanedTerminalSnapshots, terminalSnapshotStorage, and the registry additions entirely. We also shipped QUOTA_NOTICE_MODE as offer-reclaim rather than silent.

  2. We couldn't push the rework to your branch — "Allow edits from maintainers" isn't enabled on this fork PR. For future PRs, please tick that checkbox (or enable it in the sidebar under "Maintainers are allowed to edit this pull request"): it lets us rebase and land your work directly with your commits intact, instead of squashing it into a co-authored replacement like we had to here.

Closing in favor of #6038, which credits you as co-author. The remaining growth contributors you flagged (uncapped viewedFiles, heavy paneLayout payloads, per-org keys) are tracked in SUPER-1686.

Kitenite added a commit that referenced this pull request Jul 29, 2026
…ollback loops (#6038)

Reworked from PR #5965 by @snehendu098 (refs #5496). When localStorage
is full, @tanstack/db's localStorageCollectionOptions rethrows
QuotaExceededError on every write; the library rolls the optimistic
write back, the live query re-emits, the queuing effect retries -
an infinite loop (~40x/s) that pegs the renderer and survives restarts.

withQuotaGuard wraps every localStorage-backed collection's storage so
setItem absorbs quota failures: reclaim space, retry once, then drop
the write instead of throwing. No throw means no rollback, so the loop
cannot form at any write site.

Changes from the original PR:
- Reclaim is driven by the terminal GC's persisted-at index (24h
  pressure TTL, terminal-buffer-gc.ts) instead of registry membership
  plus an in-memory preserved-set that reset on relaunch and could
  delete restorable snapshots of not-yet-opened workspaces.
- Key prefixes stay canonical in terminal-buffer-gc.ts; the
  shared/constants.ts copies and the registry additions
  (getRegisteredTerminalIds, preservedSnapshotTerminalIds) are dropped.
- QUOTA_NOTICE_MODE ships as offer-reclaim: genuine exhaustion surfaces
  a toast with a working "Free up space" action instead of a silent
  console warning.

Co-authored-by: snehendu098 <snehendu098@users.noreply.github.com>
@snehendu098
snehendu098 deleted the fix/quotaexceederror-loop branch July 29, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants