{"record":{"id":"6c9c10622b5417b8","repo":"a-b-street/abstreet","slug":"no-window-cli","errorCode":null,"errorMessage":"no window?","messagePattern":"no window\\?","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"abstutil/src/cli.rs","lineNumber":48,"sourceCode":"/// between arguments. The string returned starts with `?`, unless the arguments are all empty.\npub fn args_to_query_string(args: Vec<String>) -> String {\n    // TODO Similar to parse_args forgoing a URL decoding crate, just handle this one\n    // transformation\n    let result = args\n        .into_iter()\n        .map(|x| x.replace(\" \", \"%20\"))\n        .collect::<Vec<_>>()\n        .join(\"&\");\n    if result.is_empty() {\n        result\n    } else {\n        format!(\"?{}\", result)\n    }\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn parse_args() -> anyhow::Result<Vec<String>> {\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    // Consider using a proper url parsing crate. This works fine for now, though.\n    let url_parts = url.split(\"?\").collect::<Vec<_>>();\n    if url_parts.len() != 2 {\n        bail!(\"URL {url} doesn't seem to have query params\");\n    }\n    let parts = url_parts[1]\n        .split(\"&\")\n        .map(|x| x.replace(\"%20\", \" \").to_string())\n        .collect::<Vec<_>>();\n    Ok(parts)\n}\n","sourceCodeStart":30,"sourceCodeEnd":65,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/abstutil/src/cli.rs#L30-L65","documentation":"In the wasm32 build of abstutil's CLI argument parsing, parse_args first needs the browser's global Window object via web_sys::window(). If that returns None there is no JS global scope to read the URL query string from, so the function aborts with anyhow!(\"no window?\"). This guards code that must only ever run inside a browser.","triggerScenarios":"Calling cli_args() (wasm32 target) from a WebAssembly context that is not a browser main thread with a DOM window — e.g. running in Node/wasmtime, a web worker without window shim, or before JS glue sets up globals.","commonSituations":"Testing wasm code headlessly in Node; running wasm tests outside a browser; embedding the wasm module in a worker; calling parse_args during non-DOM initialization.","solutions":["Ensure the code runs in a real browser main thread where window exists","For non-browser wasm environments, add a JS shim that defines a global window object before the module loads","Restructure to pass CLI args explicitly instead of reading window.location.href","Use #[cfg] to gate browser-only code paths and provide a non-wasm fallback"],"exampleFix":"// before\nlet window = web_sys::window().ok_or(anyhow!(\"no window?\"))?;\n// after\nlet Some(window) = web_sys::window() else {\n    return Ok(std::env::args().skip(1).collect()); // non-browser fallback\n};","handlingStrategy":"fallback","validationCode":"// before calling wasm-only arg parsing\nif web_sys::window().is_none() {\n    // non-browser environment: skip or use env::args\n}","typeGuard":"fn has_window() -> bool { web_sys::window().is_some() }","tryCatchPattern":"match cli_args() {\n    Ok(args) => use(args),\n    Err(e) if e.to_string().contains(\"no window?\") => fallback_args(),\n    Err(e) => report(e),\n}","preventionTips":["Only run wasm code paths that touch web-sys inside a browser main thread","Add an early environment check before any window-dependent API","Provide non-browser fallbacks for arg/URL parsing"],"tags":["wasm","browser-api","web-sys","environment"],"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"}