{"record":{"id":"4d8d37a76fc31979","repo":"a-b-street/abstreet","slug":"no-window-url","errorCode":null,"errorMessage":"no window?","messagePattern":"no window\\?","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"widgetry/src/tools/url.rs","lineNumber":108,"sourceCode":"\n        // But actually, the algebra shows we don't even need window_width. Easy!\n        let cam_zoom = 1.0 / horiz_meters_per_pixel;\n\n        Some((pt, cam_zoom))\n    }\n}\n\nfn must_update_url(transform: Box<dyn Fn(String) -> String>) {\n    if let Err(err) = update_url(transform) {\n        warn!(\"Couldn't update URL: {}\", err);\n    }\n}\n\n#[allow(unused_variables)]\nfn update_url(transform: Box<dyn Fn(String) -> String>) -> Result<()> {\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        let window = web_sys::window().ok_or(anyhow!(\"no window?\"))?;\n        let url = window.location().href().map_err(|err| {\n            anyhow!(err\n                .as_string()\n                .unwrap_or(\"window.location.href failed\".to_string()))\n        })?;\n        let new_url = (transform)(url);\n\n        // Setting window.location.href may seem like the obvious thing to do, but that actually\n        // refreshes the page. This method just changes the URL and doesn't mess up history. See\n        // https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API.\n        let history = window.history().map_err(|err| {\n            anyhow!(err\n                .as_string()\n                .unwrap_or(\"window.history failed\".to_string()))\n        })?;\n        history\n            .replace_state_with_url(&wasm_bindgen::JsValue::NULL, \"\", Some(&new_url))\n            .map_err(|err| {","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/widgetry/src/tools/url.rs#L90-L126","documentation":"update_url reads window.location.href to rewrite the page URL; in wasm it needs the global Window object. web_sys::window() returned None, so the library throws 'no window?'. web_sys::window() is only Some in a browser main-thread context.","triggerScenarios":"Calling must_update_url/update_url outside a browser window context: on a non-main thread, in a worker, in Node/tests targeting wasm32, or in headless wasm runtimes without DOM.","commonSituations":"Unit tests running under wasm-bindgen-test without DOM, code executing in a Web Worker, calling URL updating during early init before the DOM is ready.","solutions":["Only call must_update_url from the browser main thread with a real DOM","Guard the call with a web_sys::window().is_some() check and skip URL updates otherwise","Skip URL persistence in tests/workers — the function is already #[allow(unused_variables)] for non-wasm targets"],"exampleFix":"// before\nmust_update_url(ctx, |url| ...);\n// after\nif web_sys::window().is_some() {\n    must_update_url(ctx, |url| ...);\n}","handlingStrategy":"type-guard","validationCode":"if web_sys::window().is_none() {\n    log::warn!(\"no browser window; skipping URL update\");\n    return;\n}","typeGuard":"fn has_window() -> bool { web_sys::window().is_some() }","tryCatchPattern":"if let Err(e) = update_url(transform) {\n    log::warn!(\"URL update skipped: {:?}\", e);\n}","preventionTips":["Only call URL-updating APIs from the browser main thread","Skip URL persistence in wasm tests and workers","Gate must_update_url behind a web_sys::window().is_some() check"],"tags":["wasm","browser","dom","web-sys"],"backgroundTag":"null-argument","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"}