{"record":{"id":"ba7b29c6143a4ba2","repo":"koala73/worldmonitor","slug":"settings-export-could-not-read-browser-storage","errorCode":null,"errorMessage":"Settings export could not read browser storage.","messagePattern":"Settings export could not read browser storage\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/utils/settings-persistence.ts","lineNumber":62,"sourceCode":"\nexport function exportSettings(): void {\n  const data: Record<string, string> = {};\n\n  // Storage that cannot be read has no settings to export, and handing the\n  // user a downloadable file anyway is worse than failing: the caller in\n  // preferences-content.ts wraps this in try/catch and shows `exportSuccess`\n  // when it returns, so a silent empty payload becomes a green \"Exported\"\n  // toast over a backup containing nothing (#7833 review). Stay loud.\n  //\n  // Testing AVAILABILITY is not enough on its own, which an earlier round of\n  // this fix got wrong: a handle can exist while enumeration or an individual\n  // read throws, and the degrading accessors then return `[]`/`null` and\n  // rebuild exactly that empty-but-successful backup. The snapshot reports\n  // whether the reads themselves succeeded, so a partial one fails instead of\n  // shipping a backup the user would only discover was empty when restoring.\n  const snapshot = safeStorageSnapshot();\n  if (!snapshot.ok) {\n    throw new Error('Settings export could not read browser storage.');\n  }\n\n  let variant = 'full';\n  for (const [key, value] of snapshot.entries) {\n    if (key === 'worldmonitor-variant' && value) variant = value;\n    if (isSettingsKey(key)) data[key] = value;\n  }\n\n  const exportData: ExportedSettings = {\n    version: 1,\n    timestamp: new Date().toISOString(),\n    variant,\n    data,\n  };\n\n  const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });\n  const url = URL.createObjectURL(blob);\n  const a = document.createElement('a');","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/src/utils/settings-persistence.ts#L44-L80","documentation":"exportSettings builds a settings backup from a snapshot of the browser's storage (localStorage etc.) taken by safeStorageSnapshot(). If any underlying storage read throws — the snapshot reports ok=false rather than degrading to an empty backup — exportSettings throws so it never ships a backup that silently looks successful but is empty.","triggerScenarios":"Calling exportSettings when browser storage is unreadable: localStorage access blocked (browser privacy mode / 'Block all cookies'), StorageDisabled SecurityError in sandboxed iframes, quota/state errors, or storage poisoned by an extension that throws on property access.","commonSituations":"Users on strict privacy settings or Brave/Safari locking down storage; the app embedded in a third-party iframe with partitioned storage; automated headless browsers denying storage; corporate policies disabling site data.","solutions":["Detect storage availability up front (try a test localStorage read) and show the user an explanatory message with browser-settings guidance before exporting.","Catch this error in the UI and offer an alternative export path (e.g. export current in-memory settings state instead of storage-backed values).","If running in an iframe, ensure the document has storage access (requestStorageAccess / proper permissions policy) or export from a top-level context.","Retry the export once after the user adjusts browser settings and storage becomes readable."],"exampleFix":"// before\nconst backup = exportSettings(); // throws when storage is blocked\n// after\nlet backup;\ntry {\n  backup = exportSettings();\n} catch (err) {\n  if (err instanceof Error && err.message.includes('could not read browser storage')) {\n    notifyUser('Browser storage is blocked; enable site data or export current settings instead.');\n    backup = buildBackupFromInMemoryState();\n  } else throw err;\n}","handlingStrategy":"try-catch","validationCode":"function storageIsReadable(): boolean {\n  try {\n    const k = '__wm_probe__';\n    window.localStorage.setItem(k, '1');\n    window.localStorage.removeItem(k);\n    return true;\n  } catch {\n    return false;\n  }\n}\nif (!storageIsReadable()) warnUserBeforeExport();","typeGuard":null,"tryCatchPattern":"try {\n  const backup = exportSettings();\n} catch (err) {\n  if (err instanceof Error && err.message === 'Settings export could not read browser storage.') {\n    showStorageBlockedDialog(); // guide user to enable site data, or export in-memory state\n  } else throw err;\n}","preventionTips":["Probe storage availability at app boot and disable export/import buttons with an explanation when blocked.","Handle Safari/Brave privacy modes and third-party iframe storage partitioning explicitly.","Offer an in-memory fallback backup so users can still leave with their settings when storage is denied."],"tags":["browser-storage","storage-access","export"],"backgroundTag":"file-read-failed","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}