bevyengine/bevy · error

Cannot find element: {selector}.

Error message

Cannot find element: {selector}.

What it means

The wasm counterpart of a valid-but-unmatched selector: in winit_windows, document.querySelector succeeded syntactically but returned null, so bevy panics "Cannot find element: {selector}.". The selector is fine; the HTML document simply contains no element matching it at the time the window was created.

Source

Thrown at crates/bevy_winit/src/winit_windows.rs:284

        )]
        let mut winit_window_attributes = winit_window_attributes.with_title(window.title.as_str());

        #[cfg(target_arch = "wasm32")]
        {
            use wasm_bindgen::JsCast;
            use winit::platform::web::WindowAttributesExtWebSys;

            if let Some(selector) = &window.canvas {
                let window = web_sys::window().unwrap();
                let document = window.document().unwrap();
                let canvas = document
                    .query_selector(selector)
                    .expect("Cannot query for canvas element.");
                if let Some(canvas) = canvas {
                    let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok();
                    winit_window_attributes = winit_window_attributes.with_canvas(canvas);
                } else {
                    panic!("Cannot find element: {selector}.");
                }
            }

            winit_window_attributes =
                winit_window_attributes.with_prevent_default(window.prevent_default_event_handling);
            winit_window_attributes = winit_window_attributes.with_append(true);
        }

        let winit_window = event_loop.create_window(winit_window_attributes).unwrap();
        let name = window.title.clone();
        prepare_accessibility_for_window(
            event_loop,
            &winit_window,
            entity,
            name,
            accessibility_requested.clone(),
            adapters,
            handlers,

View on GitHub (pinned to 396ca72708)

Solutions

  1. Add the matching element to the HTML host page: <canvas id="game-canvas"></canvas>.
  2. Load the wasm binary after DOM readiness — put the script at the end of <body> or use defer/module scripts.
  3. Double-check exact spelling and case of the id/class on both sides.
  4. If the canvas is inserted dynamically, wait for it to exist before spawning the Window (e.g. gate app start on a JS readiness signal).

Example fix

<!-- before: index.html missing the element -->
<body><script src="pkg/my_game.js"></script></body>

<!-- after: canvas exists and script runs after it -->
<body>
  <canvas id="game-canvas"></canvas>
  <script type="module" src="pkg/my_game.js"></script>
</body>
Defensive patterns

Strategy: validation

Validate before calling

#[cfg(target_arch = "wasm32")]
fn canvas_exists(selector: &str) -> bool {
    web_sys::window()
        .and_then(|w| w.document())
        .and_then(|d| d.query_selector(selector).ok().flatten()) // Ok(None) = no match
        .is_some()
}

// gate window creation on DOM readiness
if !canvas_exists("#game-canvas") {
    web_sys::console::warn_1(&"#game-canvas missing — check index.html".into());
}

Try / catch

// Panic happens during window creation; probe the DOM first and defer the Window
// (or fall back to Bevy-created canvas) when the element is absent.

Prevention

When it happens

Trigger: Window { canvas: Some("#game-canvas".into()) } while index.html has no <canvas id="game-canvas">; a typo'd or renamed id; the wasm module executing before the DOM is parsed (script in <head> without defer); canvas existing only after JS dynamically inserts it later.

Common situations: Web builds where the script tag placement changed; renaming the canvas in HTML but not in Rust (or vice versa); frameworks that render the host page asynchronously; copy-pasting index.html templates that use a different id.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/f725bdd8cd068f0d. Report an issue: GitHub.