{"record":{"id":"d1c6619cedcb216f","repo":"a-b-street/abstreet","slug":"local-storage-failed","errorCode":null,"errorMessage":"local_storage failed","messagePattern":"local_storage failed","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"abstio/src/io_web.rs","lineNumber":170,"sourceCode":"}\n\npub fn delete_file<I: AsRef<str>>(path: I) {\n    let path = path.as_ref();\n    if !path.starts_with(&path_player(\"\")) {\n        warn!(\"Not deleting {}\", path);\n        return;\n    }\n    let window = web_sys::window().unwrap();\n    let storage = window.local_storage().unwrap().unwrap();\n    storage.remove_item(path).unwrap();\n}\n\nfn read_local_storage(path: &str) -> Result<String> {\n    let window = web_sys::window().ok_or(anyhow!(\"no window?\"))?;\n    let storage = window\n        .local_storage()\n        .map_err(|err| {\n            anyhow!(err\n                .as_string()\n                .unwrap_or(\"local_storage failed\".to_string()))\n        })?\n        .ok_or(anyhow!(\"no local_storage?\"))?;\n    let string = storage\n        .get_item(&path)\n        .map_err(|err| anyhow!(err.as_string().unwrap_or(\"get_item failed\".to_string())))?\n        .ok_or(anyhow!(\"{} missing from local storage\", path))?;\n    Ok(string)\n}\n\nfn list_local_storage_keys() -> Vec<String> {\n    let window = web_sys::window().unwrap();\n    let storage = window.local_storage().unwrap().unwrap();\n    let mut keys = Vec::new();\n    for idx in 0..storage.length().unwrap() {\n        keys.push(storage.key(idx).unwrap().unwrap());\n    }","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/abstio/src/io_web.rs#L152-L188","documentation":"read_local_storage maps a failure of window.local_storage() to the underlying exception's text, falling back to \"local_storage failed\" when the DomException has no message. This means the browser refused or failed the attempt to access the localStorage object itself. It is distinct from the key simply being missing.","triggerScenarios":"Calling read_local_storage (via slurp_file/maybe_read_binary) when window.local_storage() returns Err — e.g. cookies/site-data blocked, storage access denied by browser privacy settings, or the page opened in a context where storage is disabled (some private-browsing modes in older browsers).","commonSituations":"Users with strict privacy settings or extensions blocking storage; sandboxed iframes without allow-same-origin; embedded webviews with storage disabled.","solutions":["Ask the user to enable cookies/site data / disable the blocking extension for the site","Avoid sandboxed iframes or add allow-same-origin to the sandbox attribute","Log the underlying exception message (err.as_string()) to diagnose which exception occurred","Fall back to in-memory storage or IndexedDB when localStorage is unavailable"],"exampleFix":"// before\nlet raw = abstio::slurp_file(path).unwrap();\n// after\nlet raw = abstio::slurp_file(path).unwrap_or_else(|e| {\n    warn!(\"localStorage unavailable ({e}); using defaults\");\n    String::new()\n});","handlingStrategy":"try-catch","validationCode":"let storage_ok = web_sys::window()\n    .and_then(|w| w.local_storage().ok())\n    .transpose()\n    .is_ok();","typeGuard":null,"tryCatchPattern":"match slurp_file(path) {\n    Ok(v) => v,\n    Err(e) => { log::warn!(\"storage access failed: {e}\"); Default::default() }\n}","preventionTips":["Handle localStorage-unavailable environments gracefully (private mode, blockers)","Test with browser privacy settings that block site data","Avoid sandboxed iframes without allow-same-origin","Always provide an in-memory or network fallback for reads"],"tags":["wasm","localstorage","privacy","browser"],"backgroundTag":"permission-denied","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}