Summary
A QuotaExceededError: Failed to execute 'setItem' on 'Storage' can surface from various small Zustand-persisted stores (e.g., notification-storage, panel-editor-state) when a user has actively built workflows for some time. The stack trace points to the persist middleware's setItem call on whichever store happened to write next after the origin's localStorage cap was reached.
The store whose throw lands in monitoring is not the actual culprit — it's just the first victim after another store filled the origin's quota.
Root cause
workflow-undo-redo is persisted to localStorage and stores operation snapshots that include full block state. Capacity is MAX_STACKS=5 × DEFAULT_CAPACITY=100 = 500 ops. Active editing of multiple workflows quickly fills the per-stack capacity with snapshots that can be several KB each, occupying the bulk of the origin's ~5 MB localStorage budget.
Once the budget is exhausted, any other persisted Zustand store whose setItem is called next throws QuotaExceededError.
Reproduction (Chromium-based browser, ~5 MB origin cap)
- Open the workflow editor.
- Add an agent block and paste a large system prompt (e.g., 5 KB of text).
- Delete and re-add the block several times. Each cycle pushes
batch-add-blocks and batch-remove-blocks operations whose snapshots include the full block state.
- Across a few workflows (up to 5 stacks as
MAX_STACKS allows), the localStorage origin reaches the cap.
- Take any action that triggers another store's persist (e.g., select a block, dismiss a notification). The next
setItem throws QuotaExceededError.
Measurement helper for the console:
```js
const sizes = Object.keys(localStorage).map(k => [k, localStorage.getItem(k)?.length ?? 0])
console.table(sizes.sort((a, b) => b[1] - a[1]))
```
shows workflow-undo-redo occupying the bulk of origin storage in the affected state.
Existing partial mitigations
Proposed fix
Migrate `workflow-undo-redo` to IndexedDB using the same `idb-keyval` pattern as `terminal-console-store` (PR #2812), including a one-time migration that copies any existing data and removes the legacy localStorage key on first module load. PR follows.
Summary
A
QuotaExceededError: Failed to execute 'setItem' on 'Storage'can surface from various small Zustand-persisted stores (e.g.,notification-storage,panel-editor-state) when a user has actively built workflows for some time. The stack trace points to the persist middleware'ssetItemcall on whichever store happened to write next after the origin's localStorage cap was reached.The store whose throw lands in monitoring is not the actual culprit — it's just the first victim after another store filled the origin's quota.
Root cause
workflow-undo-redois persisted to localStorage and stores operation snapshots that include full block state. Capacity isMAX_STACKS=5 × DEFAULT_CAPACITY=100 = 500 ops. Active editing of multiple workflows quickly fills the per-stack capacity with snapshots that can be several KB each, occupying the bulk of the origin's ~5 MB localStorage budget.Once the budget is exhausted, any other persisted Zustand store whose
setItemis called next throwsQuotaExceededError.Reproduction (Chromium-based browser, ~5 MB origin cap)
batch-add-blocksandbatch-remove-blocksoperations whose snapshots include the full block state.MAX_STACKSallows), the localStorage origin reaches the cap.setItemthrowsQuotaExceededError.Measurement helper for the console:
```js
const sizes = Object.keys(localStorage).map(k => [k, localStorage.getItem(k)?.length ?? 0])
console.table(sizes.sort((a, b) => b[1] - a[1]))
```
shows
workflow-undo-redooccupying the bulk of origin storage in the affected state.Existing partial mitigations
safeStorageAdapterto undo-redo's persist middleware, swallowing the quota throw silently for that store — but it does not free the origin budget that other small stores share, so those still throw uncaught.terminal-console-storefrom localStorage to IndexedDB for the same reason (large data).Proposed fix
Migrate `workflow-undo-redo` to IndexedDB using the same `idb-keyval` pattern as `terminal-console-store` (PR #2812), including a one-time migration that copies any existing data and removes the legacy localStorage key on first module load. PR follows.