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
- Ensure the canvas and context are created in the same JS realm as the wasm module (no cross-iframe/worker sharing).
- Log the returned object's constructor name (js_sys::Object::to_string) to see what type actually came back.
- 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
- Create the canvas and context in the same JS realm/worker as the wasm module.
- When inspecting failures, log the returned object's constructor name to identify the actual type.
- Keep web-sys's OffscreenCanvas bindings current with target browsers.
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
- creating text OffscreenCanvas
- getting OffscreenCanvas 2D context
- setting Canvas readback option
- Canvas readback option could not be set
- Canvas returned non-finite text metrics
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)