a-b-street/abstreet · warning · anyhow::Error

window.location.href failed

Error message

window.location.href failed

What it means

After getting the window, update_url calls window.location().href() to read the current URL. This returns a Result in web-sys; on a JS exception it throws this error, falling back to 'window.location.href failed' when the JsValue error has no string form.

Solutions

  1. Embed the app in a non-sandboxed iframe or add allow-same-origin to the sandbox attribute
  2. Serve the page over http(s) from a real origin rather than file:/data:/blob:
  3. Check the browser console for the underlying SecurityError
  4. Fall back to skipping URL persistence when the origin is opaque

Example fix

// sandboxed iframe causing SecurityError
// before
<iframe sandbox="allow-scripts" src="app.html"></iframe>
// after
<iframe sandbox="allow-scripts allow-same-origin" src="app.html"></iframe>
Defensive patterns

Strategy: try-catch

Validate before calling

// opaque-origin check
let origin_ok = window.location().origin().map(|o| !o.is_empty() && o != "null").unwrap_or(false);
if !origin_ok { skip_url_update(); }

Try / catch

let url = window.location().href().map_err(|e| anyhow!(e.as_string().unwrap_or_else(|| "window.location.href failed".into())));
if url.is_err() { log::warn!("cannot read location; skipping URL update"); return Ok(()); }

Prevention

When it happens

Trigger: Reading location.href throws in the browser: unusual security contexts (sandboxed iframe denying navigation/Location access), opaque origins (e.g. sandboxed about:srcdoc frames), or exotic embedded browsers.

Common situations: App embedded in a sandboxed iframe without allow-same-origin, running from a data:/blob: origin, privacy-hardened browsers restricting location access.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/cb761ac70affbc2a. Report an issue: GitHub.

Appendix: source

Thrown at widgetry/src/tools/url.rs:110

        let cam_zoom = 1.0 / horiz_meters_per_pixel;

        Some((pt, cam_zoom))
    }
}

fn must_update_url(transform: Box<dyn Fn(String) -> String>) {
    if let Err(err) = update_url(transform) {
        warn!("Couldn't update URL: {}", err);
    }
}

#[allow(unused_variables)]
fn update_url(transform: Box<dyn Fn(String) -> String>) -> Result<()> {
    #[cfg(target_arch = "wasm32")]
    {
        let window = web_sys::window().ok_or(anyhow!("no window?"))?;
        let url = window.location().href().map_err(|err| {
            anyhow!(err
                .as_string()
                .unwrap_or("window.location.href failed".to_string()))
        })?;
        let new_url = (transform)(url);

        // Setting window.location.href may seem like the obvious thing to do, but that actually
        // refreshes the page. This method just changes the URL and doesn't mess up history. See
        // https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API.
        let history = window.history().map_err(|err| {
            anyhow!(err
                .as_string()
                .unwrap_or("window.history failed".to_string()))
        })?;
        history
            .replace_state_with_url(&wasm_bindgen::JsValue::NULL, "", Some(&new_url))
            .map_err(|err| {
                anyhow!(err
                    .as_string()

View on GitHub (pinned to 0964f29315)