jlcodes99/cockpit-tools · warning

Failed to save active page to localStorage:

Error message

Failed to save active page to localStorage:

What it means

MainApp persists the currently active page to localStorage under ACTIVE_PAGE_STORAGE_KEY inside a useEffect. If the setItem/removeItem call throws — most commonly because localStorage is unavailable — the catch logs this warning and the page still switches in memory, but the preference is not remembered across restarts.

Source

Thrown at src/App.tsx:802

        memory.dismissed[USER_MEMORY_FLAGS.classicSwitchPrompt] ||
        store.hideClassicSwitchPrompt
      ) {
        store.setHideClassicSwitchPrompt(true);
      }
    });
  }, []);

  useEffect(() => {
    try {
      const normalized = normalizeStoredActivePage(page);
      if (normalized) {
        localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);
      } else {
        localStorage.removeItem(ACTIVE_PAGE_STORAGE_KEY);
        setPage('dashboard');
      }
    } catch (e) {
      console.warn('Failed to save active page to localStorage:', e);
    }
  }, [page]);

  // 冷启动:若设置了固定启动页,则覆盖 localStorage 中的上次页面
  useEffect(() => {
    let disposed = false;
    const applyStartupPagePreference = async () => {
      try {
        const config = await invoke<{ startup_page?: string }>('get_general_config');
        if (disposed) {
          return;
        }
        const preferred = normalizeStartupPagePreference(config.startup_page);
        if (preferred !== 'last') {
          setPage(preferred);
        }
      } catch (error) {
        console.warn('Failed to apply startup page preference:', error);

View on GitHub (pinned to 1ed8b77992)

Solutions

  1. Check the logged `e` — QuotaExceededError means free up/clear stale localStorage keys; SecurityError means storage is blocked.
  2. Verify the WebView/browser isn't in private mode or blocking third-party storage.
  3. Wrap the persistence in a safe wrapper that feature-detects storage availability.
  4. Clear app localStorage data if the quota is full.

Example fix

// before
try {
  localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);
} catch (e) {
  console.warn('Failed to save active page to localStorage:', e);
}
// after
// guard before writing
const storageAvailable = (() => {
  try { localStorage.setItem('__t', '1'); localStorage.removeItem('__t'); return true; }
  catch { return false; }
})();
if (storageAvailable) localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);
Defensive patterns

Strategy: fallback

Validate before calling

const canUseLocalStorage = (() => {
  try {
    const k = '__probe__';
    localStorage.setItem(k, '1');
    localStorage.removeItem(k);
    return true;
  } catch {
    return false;
  }
})();

Type guard

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

Try / catch

try {
  localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);
} catch (e) {
  console.warn('Failed to save active page to localStorage:', e);
  inMemoryActivePage = normalized; // in-memory fallback
}

Prevention

When it happens

Trigger: Changing pages when localStorage is unavailable: browser/WebView storage disabled or full, running in a private/incognito context with storage blocked, a storage quota exceeded, or security policy (opaque origin / file:// context) blocking access.

Common situations: Tauri WebView with storage partition disabled, QuotaExceededError after lots of stored data, Safari private mode (older WebKit throws on setItem), or tests/iframes with opaque origins.

Related errors


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