zed-industries/zed · warning

render_to_image unavailable while recovering from a lost…

Error message

render_to_image unavailable while recovering from a lost device

What it means

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!.

Solutions

  1. 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.
  2. Listen for the renderer's device-recovered signal/event and only call render_to_image after it fires.
  3. Fall back to a window-capture screenshot API when device recovery is in progress.
  4. If it persists, file a bug: skip_draws should clear after recovery, so it staying set indicates recovery never completed.

Example fix

// before
let image = renderer.render_to_image(&scene, appearance)?;
// after
let image = loop {
    match renderer.render_to_image(&scene, appearance) {
        Ok(image) => break image,
        Err(e) if e.to_string().contains("recovering from a lost device") => {
            smol::Timer::after(Duration::from_millis(100)).await;
        }
        Err(e) => return Err(e),
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// caller-side guard, if the renderer exposes skip_draws
if renderer.is_recovering_from_device_loss() {
    return None; // defer the screenshot until recovery completes
}

Try / catch

match renderer.render_to_image(&scene, appearance) {
    Ok(img) => Some(img),
    Err(e) if e.to_string().contains("lost device") => {
        schedule_retry_after_device_recovery();
        None
    }
    Err(e) => { log::error!("render_to_image failed: {e:#}"); None }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at crates/gpui_windows/src/directx_renderer.rs:421

        }
        Ok(())
    }

    /// Render `scene` to an offscreen CPU image **without presenting** so
    /// the window need never be shown or visible (the macOS headless path
    /// goes through MetalRenderer; this is the Windows analogue). Draws into
    /// the existing render target, copies it into a `D3D11_USAGE_STAGING`
    /// texture, maps it, and converts BGRA to RGBA.
    #[cfg(any(test, feature = "test-support"))]
    pub(crate) fn render_to_image(
        &mut self,
        scene: &Scene,
        background_appearance: WindowBackgroundAppearance,
    ) -> Result<image::RgbaImage> {
        // A pending device-lost recovery (`skip_draws`) leaves the atlas holding
        // tile references from the previous device; drawing before the forced
        // re-render rebuilds them panics in `DirectXAtlasTextures::texture`.
        anyhow::ensure!(
            !self.skip_draws,
            "render_to_image unavailable while recovering from a lost device"
        );
        self.render(scene, background_appearance)?;

        let devices = self.devices.as_ref().context("devices missing")?;
        let device = &devices.device;
        let context = &devices.device_context;
        let resources = self.resources.as_ref().context("resources missing")?;
        let render_target = resources
            .render_target
            .as_ref()
            .context("render target missing")?;

        // A CPU-readable copy of the render target.
        let mut desc = D3D11_TEXTURE2D_DESC::default();
        unsafe { render_target.GetDesc(&mut desc) };
        let width = desc.Width;

View on GitHub (pinned to 916fc2b8cb)