jlcodes99/cockpit-tools · warning

[AccountStore] 删除持久化数据失败: ${name}

Error message

[AccountStore] 删除持久化数据失败: ${name}

What it means

This warning is logged by the removeItem handler of a zustand persist storage shim when localStorage.removeItem(name) throws. The library wraps every storage call in try/catch because localStorage can fail (privacy mode, quota, disabled storage), and rather than crash state hydration/persistence it downgrades the failure to a console.warn. The persisted key named `name` could not be deleted, so stale data may remain in storage.

Source

Thrown at src/stores/useAccountStore.ts:78

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

function toPersistedTokenSnapshot(token: TokenData): TokenData {
  return {
    access_token: '',
    refresh_token: '',
    expires_in: 0,
    expiry_timestamp: 0,
    token_type: token.token_type || 'Bearer',
    email: token.email,
    project_id: token.project_id,
    is_gcp_tos: token.is_gcp_tos,
    session_id: token.session_id,
  };
}

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Inspect the logged `error` object to identify the DOMException (SecurityError vs QuotaExceededError) and fix the environment cause
  2. Verify the WebView/browser allows localStorage (cookies/site-data settings) for the app origin
  3. Wrap the store's persist usage so storage failures degrade gracefully, or provide a custom storage fallback (e.g. in-memory) via the persist `storage` option
  4. Clear site data manually and retry the account removal flow

Example fix

// before
removeItem: (name) => {
  localStorage.removeItem(name);
}
// after
removeItem: (name) => {
  try {
    localStorage.removeItem(name);
  } catch (error) {
    console.warn(`[AccountStore] 删除持久化数据失败: ${name}`, error);
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

function isLocalStorageAvailable() {
  try {
    const k = '__test__';
    window.localStorage.setItem(k, '1');
    window.localStorage.removeItem(k);
    return true;
  } catch {
    return false;
  }
}

Type guard

function hasLocalStorage(win: Window): win is Window & { localStorage: Storage } {
  try {
    return typeof win.localStorage !== 'undefined';
  } catch {
    return false;
  }
}

Try / catch

try {
  localStorage.removeItem(name);
} catch (error) {
  console.warn(`[AccountStore] 删除持久化数据失败: ${name}`, error);
  fallbackInMemoryStore.remove(name); // degrade gracefully
}

Prevention

When it happens

Trigger: Calling removeItem on the account store persist storage while localStorage.removeItem throws: storage disabled/blocked (Safari private mode, 'Block all cookies'), SecurityError from cross-origin iframe, quota exceeded during cleanup, or storage being cleared concurrently.

Common situations: Users with strict browser privacy settings; embedded WebView with localStorage disabled; corrupted storage entries that a framework tries to clean up during rehydrate/logout flows.

Related errors


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