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

unable to cast to WebGlRenderingContext. error

Error message

unable to cast to WebGlRenderingContext. error: {:?}

What it means

After obtaining a context object from get_context, the code dyn_into::<WebGlRenderingContext>() casts it to the WebGL 1.0 type. The cast failed, meaning the object returned by the browser is not a WebGL 1.0 rendering context (e.g. it is a WebGL2 context or another type). The library throws because glow was asked for a WebGL1-backed context but got a different JS object.

Solutions

  1. Only create one context per canvas; do not request 'webgl2' before calling this
  2. Create a fresh canvas element for the WebGL 1.0 context
  3. Check that no graphics library (three.js, regl, etc.) initialized the canvas first
  4. Log js_sys::Object::to_string on the returned value to see the actual type

Example fix

// before: reuse canvas that may already have a webgl2 context
let ctx = webgl1_glow_context(&existing_canvas)?;
// after: use a dedicated canvas
let canvas = document.create_element("canvas").unwrap().dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
let ctx = webgl1_glow_context(&canvas)?;
Defensive patterns

Strategy: type-guard

Validate before calling

let kind = canvas.get_context("webgl").ok().flatten().map(|v| js_sys::Object::from(v).to_string().into());
log::info!("context type: {:?}", kind); // should be [object WebGLRenderingContext]

Type guard

fn is_webgl1(val: &wasm_bindgen::JsValue) -> bool {
    val.is_instance_of::<web_sys::WebGlRenderingContext>()
}

Try / catch

match canvas.get_context("webgl") {
    Ok(Some(v)) if v.is_instance_of::<WebGlRenderingContext>() => { /* proceed */ }
    Ok(Some(v)) => error!("unexpected context type: {:?}", v.js_typeof()),
    _ => error!("no webgl context"),
}

Prevention

When it happens

Trigger: canvas.get_context("webgl") returned a non-null object whose runtime type is not WebGlRenderingContext — typically because a 'webgl2' context was previously created on the same canvas, or a nonstandard/incorrect object was returned.

Common situations: Mixing WebGL2 and WebGL1 initialization on the same canvas (browsers return the existing context type), custom canvas wrappers or frameworks (e.g. three.js) that already grabbed the context, stale/shimmed polyfills.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

Thrown at widgetry/src/backend_glow_wasm.rs:102

            .get_context("webgl2")
            .map_err(|err| anyhow!("error getting context for WebGL 2.0: {:?}", err))?;
        let js_webgl2_context =
            maybe_context.ok_or(anyhow!("Browser doesn't support WebGL 2.0"))?;
        let webgl2_context = js_webgl2_context
            .dyn_into::<web_sys::WebGl2RenderingContext>()
            .map_err(|err| anyhow!("unable to cast to WebGl2RenderingContext. error: {:?}", err))?;
        Ok(glow::Context::from_webgl2_context(webgl2_context))
    }

    fn webgl1_glow_context(canvas: &web_sys::HtmlCanvasElement) -> Result<glow::Context> {
        let maybe_context: Option<_> = canvas
            .get_context("webgl")
            .map_err(|err| anyhow!("error getting context for WebGL 1.0: {:?}", err))?;
        let js_webgl1_context =
            maybe_context.ok_or(anyhow!("Browser doesn't support WebGL 1.0"))?;
        let webgl1_context = js_webgl1_context
            .dyn_into::<web_sys::WebGlRenderingContext>()
            .map_err(|err| anyhow!("unable to cast to WebGlRenderingContext. error: {:?}", err))?;
        Ok(glow::Context::from_webgl1_context(webgl1_context))
    }

    (
        PrerenderInnards::new(gl, is_gl2, program, Some(WindowAdapter(winit_window))),
        event_loop,
    )
}

fn webgl2_program(gl: glow::Context) -> Result<(glow::Context, glow::Program)> {
    let program = unsafe {
        build_program(
            &gl,
            include_str!("../shaders/vertex_300.glsl"),
            include_str!("../shaders/fragment_300.glsl"),
        )?
    };
    Ok((gl, program))

View on GitHub (pinned to 0964f29315)