{"record":{"id":"317063837cfa75a6","repo":"jlcodes99/cockpit-tools","slug":"failed-to-save-active-page-to-localstorage","errorCode":null,"errorMessage":"Failed to save active page to localStorage:","messagePattern":"Failed to save active page to localStorage:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/App.tsx","lineNumber":802,"sourceCode":"        memory.dismissed[USER_MEMORY_FLAGS.classicSwitchPrompt] ||\n        store.hideClassicSwitchPrompt\n      ) {\n        store.setHideClassicSwitchPrompt(true);\n      }\n    });\n  }, []);\n\n  useEffect(() => {\n    try {\n      const normalized = normalizeStoredActivePage(page);\n      if (normalized) {\n        localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);\n      } else {\n        localStorage.removeItem(ACTIVE_PAGE_STORAGE_KEY);\n        setPage('dashboard');\n      }\n    } catch (e) {\n      console.warn('Failed to save active page to localStorage:', e);\n    }\n  }, [page]);\n\n  // 冷启动：若设置了固定启动页，则覆盖 localStorage 中的上次页面\n  useEffect(() => {\n    let disposed = false;\n    const applyStartupPagePreference = async () => {\n      try {\n        const config = await invoke<{ startup_page?: string }>('get_general_config');\n        if (disposed) {\n          return;\n        }\n        const preferred = normalizeStartupPagePreference(config.startup_page);\n        if (preferred !== 'last') {\n          setPage(preferred);\n        }\n      } catch (error) {\n        console.warn('Failed to apply startup page preference:', error);","sourceCodeStart":784,"sourceCodeEnd":820,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/App.tsx#L784-L820","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the logged `e` — QuotaExceededError means free up/clear stale localStorage keys; SecurityError means storage is blocked.","Verify the WebView/browser isn't in private mode or blocking third-party storage.","Wrap the persistence in a safe wrapper that feature-detects storage availability.","Clear app localStorage data if the quota is full."],"exampleFix":"// before\ntry {\n  localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);\n} catch (e) {\n  console.warn('Failed to save active page to localStorage:', e);\n}\n// after\n// guard before writing\nconst storageAvailable = (() => {\n  try { localStorage.setItem('__t', '1'); localStorage.removeItem('__t'); return true; }\n  catch { return false; }\n})();\nif (storageAvailable) localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);","handlingStrategy":"fallback","validationCode":"const canUseLocalStorage = (() => {\n  try {\n    const k = '__probe__';\n    localStorage.setItem(k, '1');\n    localStorage.removeItem(k);\n    return true;\n  } catch {\n    return false;\n  }\n})();\n","typeGuard":"function storageGet(key: string): string | null {\n  try { return localStorage.getItem(key); } catch { return null; }\n}\n","tryCatchPattern":"try {\n  localStorage.setItem(ACTIVE_PAGE_STORAGE_KEY, normalized);\n} catch (e) {\n  console.warn('Failed to save active page to localStorage:', e);\n  inMemoryActivePage = normalized; // in-memory fallback\n}","preventionTips":["Feature-detect localStorage at app bootstrap and keep an in-memory fallback","Keep localStorage payload small to avoid QuotaExceededError","Never treat storage write failures as fatal — page state is non-critical","Avoid private-mode assumptions; test in incognito and restricted WebViews"],"tags":["localstorage","web-storage","persistence","quota"],"backgroundTag":"localstorage-unavailable","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}