zed-industries/zed · error

unexpected OffscreenCanvas 2D context

Error message

unexpected OffscreenCanvas 2D context: {error:?}

What it means

get_context_with_context_options returns an Object; TextCanvas::new dyn_into's it into OffscreenCanvasRenderingContext2d. If the returned object is not that type (e.g. it is a different context type or a WebGl2RenderingContext), dyn_into throws and this error wraps the JsValue. It is a concrete type mismatch, not an absence of context.

Solutions

  1. Ensure the canvas and context are created in the same JS realm as the wasm module (no cross-iframe/worker sharing).
  2. Log the returned object's constructor name (js_sys::Object::to_string) to see what type actually came back.
  3. Update wasm-bindgen/web-sys so the dyn_into target matches the engine's class hierarchy.

Example fix

// before
.dyn_into::<OffscreenCanvasRenderingContext2d>()
    .map_err(|error| anyhow!("unexpected OffscreenCanvas 2D context: {error:?}"))?;
// after
.dyn_into::<OffscreenCanvasRenderingContext2d>()
    .map_err(|error| {
        let kind = error.constructor().map(|c| c.name()).unwrap_or_default();
        anyhow!("unexpected OffscreenCanvas 2D context ({kind}): {error:?}")
    })?;
Defensive patterns

Strategy: type-guard

Validate before calling

function verifyContextType(ctx) {
  return ctx instanceof OffscreenCanvasRenderingContext2d;
}

Type guard

fn is_offscreen_2d_context(value: &JsValue) -> bool {
    value.dyn_ref::<OffscreenCanvasRenderingContext2d>().is_some()
}

Try / catch

let raw = canvas.get_context_with_context_options("2d", &options)?
    .context("OffscreenCanvas 2D text rendering is unavailable")?;
let context = raw.dyn_into::<OffscreenCanvasRenderingContext2d>()
    .map_err(|error| anyhow!("unexpected OffscreenCanvas 2D context: {error:?}"))
    .inspect_err(|_| log_context_constructor_name(&raw))?;

Prevention

When it happens

Trigger: The canvas returns a context object whose JS class is not OffscreenCanvasRenderingContext2d despite requesting "2d" — typically only when the request string or engine behavior yields WebGL/other context, or wasm-bindgen's expected type doesn't match the engine's object identity (e.g. context from a different realm/iframe).

Common situations: Context created in a different JS realm (iframe/worker bridging) so wasm-bindgen's instanceof check fails; a browser quirk returning a wrapper class; accidental future change of the requested context type string.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of zed-industries/zed@916fc2b8cb (2026-09-19). Data as JSON: /api/errors/9b3ec0650b08577b. Report an issue: GitHub.

Appendix: source

Thrown at crates/gpui_web/src/canvas_text.rs:46

impl TextCanvas {
    fn new() -> Result<Self> {
        let canvas = OffscreenCanvas::new(1, 1)
            .map_err(|error| anyhow!("creating text OffscreenCanvas: {error:?}"))?;
        let options = js_sys::Object::new();
        // Every rasterized glyph is read back for upload into GPUI's atlas.
        let assigned = js_sys::Reflect::set(
            &options,
            &JsValue::from_str("willReadFrequently"),
            &JsValue::TRUE,
        )
        .map_err(|error| anyhow!("setting Canvas readback option: {error:?}"))?;
        ensure!(assigned, "Canvas readback option could not be set");
        let context = canvas
            .get_context_with_context_options("2d", &options)
            .map_err(|error| anyhow!("getting OffscreenCanvas 2D context: {error:?}"))?
            .context("OffscreenCanvas 2D text rendering is unavailable")?
            .dyn_into::<OffscreenCanvasRenderingContext2d>()
            .map_err(|error| anyhow!("unexpected OffscreenCanvas 2D context: {error:?}"))?;
        Ok(Self { canvas, context })
    }

    fn configure(&self, css_font: &str) -> Result<()> {
        ensure!(!css_font.trim().is_empty(), "Canvas text font is empty");
        // An invalid CSS font assignment is ignored by Canvas. Reset first so
        // it cannot accidentally inherit the preceding request's font.
        self.context.set_font("10px sans-serif");
        self.context.set_font(css_font);
        self.context.set_text_align("left");
        self.context.set_text_baseline("alphabetic");
        self.context.set_fill_style_str("white");
        // web-sys does not expose direction on the offscreen 2D context.
        let assigned = js_sys::Reflect::set(
            self.context.as_ref(),
            &JsValue::from_str("direction"),
            &JsValue::from_str("ltr"),
        )

View on GitHub (pinned to 916fc2b8cb)