{"record":{"id":"3e130e62ce0f013e","repo":"zed-industries/zed","slug":"render-to-image-unavailable-while-recovering-from-a-lost","errorCode":null,"errorMessage":"render_to_image unavailable while recovering from a lost device","messagePattern":"render_to_image unavailable while recovering from a lost device","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/gpui_windows/src/directx_renderer.rs","lineNumber":421,"sourceCode":"        }\n        Ok(())\n    }\n\n    /// Render `scene` to an offscreen CPU image **without presenting** so\n    /// the window need never be shown or visible (the macOS headless path\n    /// goes through MetalRenderer; this is the Windows analogue). Draws into\n    /// the existing render target, copies it into a `D3D11_USAGE_STAGING`\n    /// texture, maps it, and converts BGRA to RGBA.\n    #[cfg(any(test, feature = \"test-support\"))]\n    pub(crate) fn render_to_image(\n        &mut self,\n        scene: &Scene,\n        background_appearance: WindowBackgroundAppearance,\n    ) -> Result<image::RgbaImage> {\n        // A pending device-lost recovery (`skip_draws`) leaves the atlas holding\n        // tile references from the previous device; drawing before the forced\n        // re-render rebuilds them panics in `DirectXAtlasTextures::texture`.\n        anyhow::ensure!(\n            !self.skip_draws,\n            \"render_to_image unavailable while recovering from a lost device\"\n        );\n        self.render(scene, background_appearance)?;\n\n        let devices = self.devices.as_ref().context(\"devices missing\")?;\n        let device = &devices.device;\n        let context = &devices.device_context;\n        let resources = self.resources.as_ref().context(\"resources missing\")?;\n        let render_target = resources\n            .render_target\n            .as_ref()\n            .context(\"render target missing\")?;\n\n        // A CPU-readable copy of the render target.\n        let mut desc = D3D11_TEXTURE2D_DESC::default();\n        unsafe { render_target.GetDesc(&mut desc) };\n        let width = desc.Width;","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/zed-industries/zed/blob/916fc2b8cb3a815cbef4a3b40e13081be72036b6/crates/gpui_windows/src/directx_renderer.rs#L403-L439","documentation":"render_to_image is an offscreen-render API that must not run while the DirectX renderer is recovering from a lost GPU device. During recovery the `skip_draws` flag is set and the atlas still holds tile references belonging to the old device; drawing before the forced re-render would panic in DirectXAtlasTextures::texture. The renderer therefore rejects the call up front with this error via anyhow::ensure!.","triggerScenarios":"Calling render_to_image (e.g. for screenshots/exports) between a device-lost event and completion of the forced re-render that rebuilds atlas tiles, i.e. any time `skip_draws` is still true.","commonSituations":"Taking a screenshot right after the machine resumes from sleep/hibernate, after driver reset (TDR), after GPU driver upgrade, or on remote-desktop/session reconnection — any event that invalidates the D3D device.","solutions":["Retry render_to_image after the device recovery completes (the forced re-render clears skip_draws); poll or await the renderer's recovery before retrying.","Listen for the renderer's device-recovered signal/event and only call render_to_image after it fires.","Fall back to a window-capture screenshot API when device recovery is in progress.","If it persists, file a bug: skip_draws should clear after recovery, so it staying set indicates recovery never completed."],"exampleFix":"// before\nlet image = renderer.render_to_image(&scene, appearance)?;\n// after\nlet image = loop {\n    match renderer.render_to_image(&scene, appearance) {\n        Ok(image) => break image,\n        Err(e) if e.to_string().contains(\"recovering from a lost device\") => {\n            smol::Timer::after(Duration::from_millis(100)).await;\n        }\n        Err(e) => return Err(e),\n    }\n};","handlingStrategy":"retry","validationCode":"// caller-side guard, if the renderer exposes skip_draws\nif renderer.is_recovering_from_device_loss() {\n    return None; // defer the screenshot until recovery completes\n}","typeGuard":null,"tryCatchPattern":"match renderer.render_to_image(&scene, appearance) {\n    Ok(img) => Some(img),\n    Err(e) if e.to_string().contains(\"lost device\") => {\n        schedule_retry_after_device_recovery();\n        None\n    }\n    Err(e) => { log::error!(\"render_to_image failed: {e:#}\"); None }\n}","preventionTips":["Trigger screenshots/exports from device-recovered events, not immediately after resume/sleep","Add bounded retry with backoff around render_to_image","Watch for this error persisting — it indicates device recovery never completed and should be reported"],"tags":["directx","gpu","device-lost","rendering","windows"],"backgroundTag":"invalid-state-transition","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"}