Skip to content

fix: lazy-load storage cache on set() to prevent null spread#38

Merged
BitHighlander merged 1 commit intodevelopfrom
fix/storage-null-cache-iterable
Apr 20, 2026
Merged

fix: lazy-load storage cache on set() to prevent null spread#38
BitHighlander merged 1 commit intodevelopfrom
fix/storage-null-cache-iterable

Conversation

@BitHighlander
Copy link
Copy Markdown
Collaborator

Summary

  • Fixes TypeError: a is not iterable thrown from customStorage.addEvent (and any other storage updater that spreads prev) when the MV3 service worker wakes up and calls set(...) before the async initial load of cache resolves.
  • Observed in the wild on eth_signTypedData_v4: requestStorage.addEvent failed, but the ethereum handler continued to requireApproval(...) anyway — popup opened for a request that was never persisted, and the signing flow hung in a bad state.

Root cause

packages/storage/lib/base.ts initializes cache to null and populates it via a fire-and-forget _getDataFromStorage().then(...). In MV3 service workers that get torn down frequently, set() can be called before that promise resolves. The updater then runs with prev = null, and [...null, event] throws.

Fix

In set(), if cache === null, await _getDataFromStorage() first so the updater always receives a real value (the on-disk value or the configured fallback).

Test plan

  • Reload extension, trigger eth_signTypedData_v4 on a dapp, confirm the approval popup shows the request (no TypeError in background console).
  • Confirm eth_sendTransaction / personal_sign flows still populate requestStorage → approval → completed.
  • Confirm cold-start (kill service worker, reissue a request immediately) no longer drops the first event.

🤖 Generated with Claude Code

When an MV3 service worker wakes up and calls set() with an updater
(e.g. requestStorage.addEvent spreading prev into a new array), the
in-memory cache may still be null because _getDataFromStorage() is
async. The updater then runs with prev = null and [...null] throws
"TypeError: a is not iterable", aborting addEvent and leaving the
approval flow in a bad state (popup opens for a request that was
never persisted).

Load the cache synchronously-ish (await from chrome.storage) in set()
if it's still null, so every updater receives a real value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@BitHighlander BitHighlander merged commit 0b9c451 into develop Apr 20, 2026
4 of 5 checks passed
@BitHighlander BitHighlander deleted the fix/storage-null-cache-iterable branch April 20, 2026 23:57
@BitHighlander BitHighlander mentioned this pull request Apr 21, 2026
4 tasks
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.

1 participant