emilk/egui · error

should have a Window

Error message

should have a Window

What it means

now_sec in eframe's web module calls web_sys::window(), which returns None when there is no global Window object, and panics with 'should have a Window'. This function is only valid inside a browser (WASM) context where a window exists.

Source

Thrown at crates/eframe/src/web/mod.rs:105

}

/// Focus the given element without scrolling it into view.
///
/// Scrolling the element into view would scroll the whole page when
/// the app is embedded in a larger scrollable page,
/// see <https://github.com/emilk/egui/issues/8295>.
pub(crate) fn focus_without_scroll(element: &web_sys::HtmlElement) -> Result<(), JsValue> {
    let options = web_sys::FocusOptions::new();
    options.set_prevent_scroll(true);
    element.focus_with_options(&options)
}

/// Current time in seconds (since undefined point in time).
///
/// Monotonically increasing.
pub fn now_sec() -> f64 {
    web_sys::window()
        .expect("should have a Window")
        .performance()
        .expect("should have a Performance")
        .now()
        / 1000.0
}

/// The native GUI scale factor, taking into account the browser zoom.
///
/// Corresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.
pub fn native_pixels_per_point() -> f32 {
    let pixels_per_point = web_sys::window().unwrap().device_pixel_ratio() as f32;
    if pixels_per_point > 0.0 && pixels_per_point.is_finite() {
        pixels_per_point
    } else {
        1.0
    }
}

View on GitHub (pinned to 441971a776)

Solutions

  1. Run the code in a browser main thread context where `window` exists.
  2. In workers, construct or access a WindowProxy/DedicatedWorkerGlobalScope instead, or avoid eframe's timing helpers there.
  3. For tests, stub time functions or run tests with wasm-bindgen-test in a browser rather than native test runners.

Example fix

// before
let t = eframe::web::now_sec();
// after
let t = web_sys::window().map(|w| w.performance().map(|p| p.now() / 1000.0)).flatten().unwrap_or(0.0);
Defensive patterns

Strategy: fallback

Validate before calling

let has_window = js_sys::global().dyn_into::<web_sys::Window>().is_ok();
if !has_window { /* skip now_sec-dependent path */ }

Type guard

fn window_available() -> bool { web_sys::window().is_some() }

Try / catch

// Panic-based; wrap calls in std::panic::catch_unwind(AssertUnwindSafe(|| eframe::web::now_sec())) if recovery is essential, else guard with window().is_some().

Prevention

When it happens

Trigger: Calling eframe web code (now_sec directly, or indirectly via AppRunner::auto_save_if_needed / save / time tracking) in a non-browser WASM environment such as Node.js, a worker without a window, or running the crate natively.

Common situations: Unit tests running on native targets or in Node; WASM code running in a Web Worker that lacks a global window; accidentally compiling eframe's web backend into a server-side WASM binary.

Understand the failure class

Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.

Related errors


AI-assisted analysis of emilk/egui@441971a776 (2026-09-12). Data as JSON: /api/errors/f650d2c359b242c9. Report an issue: GitHub.