{"record":{"id":"bc371d799cf44ee9","repo":"GraphiteEditor/Graphite","slug":"gpu-executor-should-be-available-when-we-receive-a","errorCode":null,"errorMessage":"GPU executor should be available when we receive a texture","messagePattern":"GPU executor should be available when we receive a texture","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"editor/src/node_graph_executor/runtime.rs","lineNumber":262,"sourceCode":"\t\t\t\t\t\tself.process_monitor_nodes(&mut responses, self.update_thumbnails);\n\t\t\t\t\t}\n\t\t\t\t\tself.update_thumbnails = false;\n\n\t\t\t\t\t// Resolve the result from the inspection by accessing the monitor node\n\t\t\t\t\tlet inspect_result = self.inspect_state.as_ref().and_then(|state| state.access(&self.executor));\n\n\t\t\t\t\tlet (result, texture) = match result {\n\t\t\t\t\t\tOk(TaggedValue::RenderOutput(RenderOutput {\n\t\t\t\t\t\t\tdata: RenderOutputType::Texture(texture),\n\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t})) if render_config.for_export => {\n\t\t\t\t\t\t\tlet executor = self\n\t\t\t\t\t\t\t\t.editor_api\n\t\t\t\t\t\t\t\t.application_io\n\t\t\t\t\t\t\t\t.as_ref()\n\t\t\t\t\t\t\t\t.unwrap()\n\t\t\t\t\t\t\t\t.gpu_executor()\n\t\t\t\t\t\t\t\t.expect(\"GPU executor should be available when we receive a texture\");\n\n\t\t\t\t\t\t\tlet raster_cpu = Raster::new_gpu(texture).convert(Footprint::BOUNDLESS, executor).await;\n\n\t\t\t\t\t\t\tlet (data, width, height) = raster_cpu.to_flat_u8();\n\n\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\tOk(TaggedValue::RenderOutput(RenderOutput {\n\t\t\t\t\t\t\t\t\tdata: RenderOutputType::Buffer { data, width, height },\n\t\t\t\t\t\t\t\t\tmetadata,\n\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\tNone,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tOk(TaggedValue::RenderOutput(RenderOutput {\n\t\t\t\t\t\t\tdata: RenderOutputType::Texture(texture),\n\t\t\t\t\t\t\tmetadata: _,\n\t\t\t\t\t\t})) if render_config.for_eyedropper => {\n\t\t\t\t\t\t\tlet executor = self","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/node_graph_executor/runtime.rs#L244-L280","documentation":"Graphite's node graph runtime asserts an invariant when an export render (`render_config.for_export`) returns a GPU-backed `RenderOutputType::Texture`: the texture must be read back to CPU via `Raster::new_gpu(texture).convert(.., executor)`, which needs the `WgpuExecutor` held by `PlatformApplicationIo`. The `.expect` panics when a texture was produced but `gpu_executor()` returned `None`. The runtime already downgrades raster exports to the SVG pipeline when `gpu_executor().is_none()` (runtime.rs:234-238), so hitting this expect means that guard was bypassed or the graph emitted a texture anyway.","triggerScenarios":"Sending an `ExecutionRequest` with `for_export: true` and `ExportFormat::Raster` on a build where `PlatformApplicationIo::new()` failed to create a `WgpuExecutor` (`WgpuExecutor::new()` returns None when `navigator.gpu` is missing, adapter/device request fails, or the `wgpu` cargo feature is compiled out, in which case `PlatformApplicationIo::default()` has no executor at all); or a graph that returns `RenderOutputType::Texture` from a cached/stubbed texture without live GPU execution.","commonSituations":"Running the wasm editor in a browser without WebGPU (older Firefox/Safari), on a GPU blocklisted by wgpu, or inside a Web Worker where `navigator.gpu` is unavailable; embedding the wrapper and constructing `PlatformEditorApi` yourself with `PlatformApplicationIo::default()`; mismatched feature flags where the `wgpu` feature is off but texture-producing nodes still execute.","solutions":["Verify WebGPU is actually available before requesting a raster export: in the embedding page check `navigator.gpu` and `await navigator.gpu.requestAdapter() !== null`; if absent, keep the SVG fallback path (RenderMode::SvgPreview) that runtime.rs:234 already selects.","If you build the wrapper yourself, confirm the `wgpu` (and on wasm, `gpu`) cargo features are enabled for every crate in the workspace so `PlatformApplicationIo` actually stores an executor.","If you control the runtime, replace the `.expect` with a `let ... else` that logs and returns an error `ExecutionResponse` so the editor reports a failed export instead of panicking.","Ensure `PlatformEditorApi.application_io` is always the instance created by `PlatformApplicationIo::new().await`, never `Default::default()`."],"exampleFix":"// before\nlet executor = self\n\t.editor_api\n\t.application_io\n\t.as_ref()\n\t.unwrap()\n\t.gpu_executor()\n\t.expect(\"GPU executor should be available when we receive a texture\");\nlet raster_cpu = Raster::new_gpu(texture).convert(Footprint::BOUNDLESS, executor).await;\n\n// after\nlet Some(executor) = self.editor_api.application_io.as_ref().unwrap().gpu_executor() else {\n  error!(\"Export produced a texture but no GPU executor exists; failing export gracefully\");\n  self.sender.send_execution_response(ExecutionResponse {\n    execution_id,\n    result: Err(\"GPU unavailable for texture export\".to_string()),\n    responses: VecDeque::new(),\n    vector_modify: Default::default(),\n    inspect_result: None,\n  });\n  continue;\n};\nlet raster_cpu = Raster::new_gpu(texture).convert(Footprint::BOUNDLESS, executor).await;","handlingStrategy":"validation","validationCode":"// TS: gate GPU-dependent export before invoking the wrapper\nexport async function webGpuAvailable(): Promise<boolean> {\n  if (!('gpu' in navigator)) return false;\n  try {\n    const adapter = await navigator.gpu.requestAdapter();\n    return adapter !== null;\n  } catch {\n    return false;\n  }\n}\n// Enable raster export only when webGpuAvailable() is true; otherwise keep the SVG fallback path.","typeGuard":null,"tryCatchPattern":"Wasm panics cannot be caught from JS. After any export attempt, check `await editor.hasCrashed()`; if true, surface an error dialog and reinitialize the editor rather than issuing further commands.","preventionTips":["Probe navigator.gpu + requestAdapter() at boot and remember the result; branch UI on it.","Keep the wgpu/gpu cargo features consistent with the browsers you deploy to.","Never construct PlatformEditorApi with a Default application_io in embedders."],"tags":["webgpu","wgpu","export","texture-readback","panic","rust","node-graph"],"backgroundTag":"webgpu-adapter-unavailable","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}