Skip to content

perf: globalState write-through causes extension host freezes and OOM pause on large task histories #1541

Description

@edelauna

Summary

The TaskHistoryStore correctly persists history items as individual JSON files on disk. However, ClineProvider also maintains a 5-second debounced write-through of the full getAll() array back into VS Code's globalState("taskHistory"). On any session with many or large tasks, this blob exceeds VS Code's recommended size, causing repeated extension host freezes and a "Pausing before OOM" self-protective pause.

Reproduction

Run a multi-child orchestrator session with 10+ tasks (or tasks with large completionResultSummary fields). Observe in the browser devtools console:

WARN [mainThreadStorage] large extension state detected
(ZooCodeOrganization.zoo-code, global: true): 2885 KB
Consider to use 'storageUri' or 'globalStorageUri' to store this data on disk instead.

This warning fires on every debounced write (every 5 s while a task is active), and on flushGlobalStateWriteThrough it fires synchronously. The extension host logs unresponsive / responsive pairs around each flush as it blocks serializing the blob over the RPC bridge. The webview shows "Pausing before OOM" when the blob approaches the size limit.

Root cause

ClineProvider.ts:249

private static readonly GLOBAL_STATE_WRITE_THROUGH_DEBOUNCE_MS = 5000

ClineProvider.ts:3440-3470scheduleGlobalStateWriteThrough / flushGlobalStateWriteThrough

Every task update calls taskHistoryStore.atomicReadAndUpdate(), which calls the onWrite callback, which schedules the write-through. Each write serializes all history items (including full task, completionResultSummary, childIds, etc.) into a single VS Code key-value entry. A 13-task session with rich summaries produces a ~2.9 MB blob.

The write-through exists because some code paths still fall back to reading globalState("taskHistory") (e.g. ClineProvider.ts:1988, 2251, 2521) and webview hydration reads it from state.

Impact

  • Extension host becomes unresponsive for several seconds on every flush
  • Triggers the extension's own OOM guard ("Pausing before OOM") on large sessions
  • Grows unboundedly — each new task or updated completionResultSummary increases the blob permanently
  • Worst for orchestrator / multi-child sessions, which produce the largest histories

Proposed fix

Remove the globalState write-through entirely. Primary storage is already file-based. Update the remaining getGlobalState("taskHistory") fallback reads (ClineProvider.ts:1988, 2251, 2521) and webview hydration to read from TaskHistoryStore directly. Remove taskHistory from the GlobalState type.

A lighter intermediate step: write only a minimal index (id, ts, status, short task snippet) to globalState and always fetch full items from disk.

Prior art

Upstream Roo-Code tracked the same root cause in RooCodeInc/Roo-Code #3784. Zoo Code already migrated primary storage to files; this issue tracks the remaining write-through that still causes the problem.

Environment

  • Zoo Code 3.80.1
  • VS Code Remote (WSL2)
  • Observed globalState blob size: ~2885 KB (well above the ~1 MB threshold where VS Code warns)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions