jlcodes99/cockpit-tools · info

[AccountStore] 读取持久化数据失败: ${name}

Error message

[AccountStore] 读取持久化数据失败: ${name}

What it means

The zustand persist custom storage's getItem wraps localStorage.getItem in try/catch. When reads fail, it warns with the storage key name and returns null so zustand falls back to the initial state instead of crashing store creation.

Source

Thrown at src/stores/useAccountStore.ts:52

  setTimeout(() => {
    try {
      localStorage.removeItem(storageKey);
      localStorage.removeItem(LEGACY_ACCOUNTS_CACHE_KEY);
      localStorage.removeItem(LEGACY_CURRENT_ACCOUNT_CACHE_KEY);
    } catch (error) {
      console.warn('[AccountStore] 清理超限缓存失败:', error);
    } finally {
      accountStoreQuotaCleanupScheduled = false;
    }
  }, 0);
}

const accountStoreStorage = createJSONStorage(() => ({
  getItem: (name: string) => {
    try {
      return localStorage.getItem(name);
    } catch (error) {
      console.warn(`[AccountStore] 读取持久化数据失败: ${name}`, error);
      return null;
    }
  },
  setItem: (name: string, value: string) => {
    try {
      localStorage.setItem(name, value);
    } catch (error) {
      if (isQuotaExceededError(error)) {
        if (!accountStoreQuotaWarned) {
          console.warn(
            '[AccountStore] 本地缓存空间不足,已自动清理账号缓存并回退为仅内存态。',
            error
          );
          accountStoreQuotaWarned = true;
        }
        scheduleAccountStoreQuotaRecovery(name);
        return;
      }

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Return null (already done) so the store boots with defaults — verify initial state is sensible
  2. Detect storage availability at startup and switch to an in-memory storage adapter
  3. Ask users to allow site data / exit private mode if persistence matters
  4. Persist to an alternative backend (IndexedDB, Tauri fs) when localStorage is unavailable

Example fix

// before
getItem: (name: string) => {
  try { return localStorage.getItem(name); }
  catch (error) { console.warn(`[AccountStore] 读取持久化数据失败: ${name}`, error); return null; }
}
// after
getItem: (name: string) => {
  try { return localStorage.getItem(name); }
  catch (error) { console.warn(`[AccountStore] 读取持久化数据失败: ${name}`, error); return memoryStore.get(name) ?? null; }
}
Defensive patterns

Strategy: fallback

Validate before calling

function safeGetItem(name: string): string | null {
  try { return localStorage.getItem(name); } catch { return null; }
}

Type guard

function hasStorage(s: unknown): s is Storage {
  try {
    const st = s as Storage;
    const k = '__probe__';
    st.setItem(k, k); st.removeItem(k);
    return true;
  } catch { return false; }
}

Try / catch

getItem: (name: string) => {
  try { return localStorage.getItem(name); }
  catch (error) {
    console.warn(`[AccountStore] 读取持久化数据失败: ${name}`, error);
    return null; // zustand falls back to initial state
  }
}

Prevention

When it happens

Trigger: localStorage.getItem throws on store initialization — storage disabled by browser policy, Safari private mode, security restrictions in embedded webviews, or corrupted storage access after a quota error.

Common situations: App opened in private/incognito browsing; enterprise browsers with third-party storage blocked; webview embedders that disable DOM storage.

Related errors


AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05). Data as JSON: /api/errors/76187109e17ddb7d. Report an issue: GitHub.