{"record":{"id":"f929761b3db0cb3b","repo":"a-b-street/abstreet","slug":"no-window","errorCode":null,"errorMessage":"no window?","messagePattern":"no window\\?","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"abstio/src/io_web.rs","lineNumber":166,"sourceCode":"            encoded.len()\n        )))\n    })?;\n    Ok(())\n}\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();","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/abstio/src/io_web.rs#L148-L184","documentation":"read_local_storage on wasm requires a browser window; web_sys::window() returning None means the code is executing outside a browser document context. The library maps that None to the \"no window?\" error instead of panicking. It happens before localStorage is even touched.","triggerScenarios":"Calling abstio::slurp_file or maybe_read_binary (which call read_local_storage) from a wasm build running in a non-browser context: a worker without a window, a Node/wasm-time test harness, or code executing before the document exists.","commonSituations":"Unit tests of wasm-targeted code run natively or in headless runtimes without a DOM; service workers / web workers where window is undefined; calling storage APIs during early startup before the page context is ready.","solutions":["Only call localStorage-backed APIs from the main browser thread/document context","Route reads through a message channel to the main thread when in a worker","In tests, stub web_sys::window or feature-gate the storage path","Check web_sys::window().is_some() in your own code before invoking the API"],"exampleFix":"// before\nlet data = abstio::slurp_file(path); // panics/errors in worker\n// after\nlet data = if web_sys::window().is_some() {\n    abstio::slurp_file(path)\n} else {\n    // worker fallback: fetch from network or postMessage to main thread\n};","handlingStrategy":"type-guard","validationCode":"let in_browser = web_sys::window().is_some();","typeGuard":"fn has_window() -> bool { web_sys::window().is_some() }","tryCatchPattern":"let raw = if has_window() {\n    slurp_file(path).unwrap_or_default()\n} else {\n    // worker/native fallback\n    fetch_or_default(path)\n};","preventionTips":["Only touch localStorage from the main browser thread/document context","Post storage reads to the main thread from workers","Feature-gate storage code paths in tests","Check web_sys::window() once at startup and choose a storage backend"],"tags":["wasm","browser","window","localstorage"],"backgroundTag":"unsupported-platform","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"}