{"record":{"id":"9b3ec0650b08577b","repo":"zed-industries/zed","slug":"unexpected-offscreencanvas-2d-context-error","errorCode":null,"errorMessage":"unexpected OffscreenCanvas 2D context: {error:?}","messagePattern":"unexpected OffscreenCanvas 2D context: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/gpui_web/src/canvas_text.rs","lineNumber":46,"sourceCode":"impl TextCanvas {\n    fn new() -> Result<Self> {\n        let canvas = OffscreenCanvas::new(1, 1)\n            .map_err(|error| anyhow!(\"creating text OffscreenCanvas: {error:?}\"))?;\n        let options = js_sys::Object::new();\n        // Every rasterized glyph is read back for upload into GPUI's atlas.\n        let assigned = js_sys::Reflect::set(\n            &options,\n            &JsValue::from_str(\"willReadFrequently\"),\n            &JsValue::TRUE,\n        )\n        .map_err(|error| anyhow!(\"setting Canvas readback option: {error:?}\"))?;\n        ensure!(assigned, \"Canvas readback option could not be set\");\n        let context = canvas\n            .get_context_with_context_options(\"2d\", &options)\n            .map_err(|error| anyhow!(\"getting OffscreenCanvas 2D context: {error:?}\"))?\n            .context(\"OffscreenCanvas 2D text rendering is unavailable\")?\n            .dyn_into::<OffscreenCanvasRenderingContext2d>()\n            .map_err(|error| anyhow!(\"unexpected OffscreenCanvas 2D context: {error:?}\"))?;\n        Ok(Self { canvas, context })\n    }\n\n    fn configure(&self, css_font: &str) -> Result<()> {\n        ensure!(!css_font.trim().is_empty(), \"Canvas text font is empty\");\n        // An invalid CSS font assignment is ignored by Canvas. Reset first so\n        // it cannot accidentally inherit the preceding request's font.\n        self.context.set_font(\"10px sans-serif\");\n        self.context.set_font(css_font);\n        self.context.set_text_align(\"left\");\n        self.context.set_text_baseline(\"alphabetic\");\n        self.context.set_fill_style_str(\"white\");\n        // web-sys does not expose direction on the offscreen 2D context.\n        let assigned = js_sys::Reflect::set(\n            self.context.as_ref(),\n            &JsValue::from_str(\"direction\"),\n            &JsValue::from_str(\"ltr\"),\n        )","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/zed-industries/zed/blob/916fc2b8cb3a815cbef4a3b40e13081be72036b6/crates/gpui_web/src/canvas_text.rs#L28-L64","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\n.dyn_into::<OffscreenCanvasRenderingContext2d>()\n    .map_err(|error| anyhow!(\"unexpected OffscreenCanvas 2D context: {error:?}\"))?;\n// after\n.dyn_into::<OffscreenCanvasRenderingContext2d>()\n    .map_err(|error| {\n        let kind = error.constructor().map(|c| c.name()).unwrap_or_default();\n        anyhow!(\"unexpected OffscreenCanvas 2D context ({kind}): {error:?}\")\n    })?;","handlingStrategy":"type-guard","validationCode":"function verifyContextType(ctx) {\n  return ctx instanceof OffscreenCanvasRenderingContext2d;\n}","typeGuard":"fn is_offscreen_2d_context(value: &JsValue) -> bool {\n    value.dyn_ref::<OffscreenCanvasRenderingContext2d>().is_some()\n}","tryCatchPattern":"let raw = canvas.get_context_with_context_options(\"2d\", &options)?\n    .context(\"OffscreenCanvas 2D text rendering is unavailable\")?;\nlet context = raw.dyn_into::<OffscreenCanvasRenderingContext2d>()\n    .map_err(|error| anyhow!(\"unexpected OffscreenCanvas 2D context: {error:?}\"))\n    .inspect_err(|_| log_context_constructor_name(&raw))?;","preventionTips":["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."],"tags":["web","wasm","type-mismatch","canvas"],"backgroundTag":"type-mismatch","analyzedSha":"916fc2b8cb3a815cbef4a3b40e13081be72036b6","analyzedAt":"2026-09-19T19:09:50.599Z","contentChangedAt":"2026-09-19T19:09:50.599Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}