{"record":{"id":"0198da23b1d37c89","repo":"linebender/druid","slug":"failed-to-update-cairo-drawable","errorCode":null,"errorMessage":"Failed to update cairo drawable: {}","messagePattern":"Failed to update cairo drawable: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"druid-shell/src/backend/x11/window.rs","lineNumber":766,"sourceCode":"            self.with_handler(|h| h.scale(scale));\n        }\n        Ok(())\n    }\n\n    // Ensure that our cairo context is targeting the right drawable, allocating one if necessary.\n    fn update_cairo_surface(&self) -> Result<(), Error> {\n        let mut buffers = borrow_mut!(self.buffers)?;\n        let pixmap = if let Some(p) = buffers.idle_pixmaps.last() {\n            *p\n        } else {\n            info!(\"ran out of idle pixmaps, creating a new one\");\n            buffers.create_pixmap(self.app.connection(), self.id)?\n        };\n\n        let drawable = XCBDrawable(pixmap);\n        borrow_mut!(self.cairo_surface)?\n            .set_drawable(&drawable, buffers.width as i32, buffers.height as i32)\n            .map_err(|e| anyhow!(\"Failed to update cairo drawable: {}\", e))?;\n        Ok(())\n    }\n\n    fn render(&self) -> Result<(), Error> {\n        self.with_handler(|h| h.prepare_paint());\n\n        if self.destroyed() {\n            return Ok(());\n        }\n\n        self.update_cairo_surface()?;\n        let invalid = std::mem::replace(&mut *borrow_mut!(self.invalid)?, Region::EMPTY);\n        {\n            let surface = borrow!(self.cairo_surface)?;\n            let cairo_ctx = cairo::Context::new(&*surface).unwrap();\n            let scale = self.scale.get();\n            for rect in invalid.rects() {\n                let rect = rect.to_px(scale).round();","sourceCodeStart":748,"sourceCodeEnd":784,"githubUrl":"https://github.com/linebender/druid/blob/0f8b1195e4e073f9597f2865299c3d18f8e4005f/druid-shell/src/backend/x11/window.rs#L748-L784","documentation":"This error is raised in `update_cairo_surface` when the cairo surface refuses to switch its backing XCB drawable to the newly created pixmap for the window. It wraps the underlying cairo error (via anyhow) after `set_drawable` fails, typically because the pixmap is invalid, the connection is broken, or cairo is in an error state. It means druid-shell cannot keep the render target in sync with the X11 buffer pool, so the frame cannot be drawn.","triggerScenarios":"`Window::render` calls `update_cairo_surface` after creating/resizing pixmaps via `buffers.create_pixmap`, and `cairo_surface.set_drawable(&XCBDrawable(pixmap), width, height)` returns Err — e.g. after a window resize produces an invalid pixmap, or the X server connection died.","commonSituations":"Window resize races on X11, X server disconnects or crashes mid-frame, running in environments where XCB/GLX state is stale (e.g. SSH-forwarded displays, nested X servers like Xvfb with limited extensions).","solutions":["Check that the X11 connection is alive and the window id is still valid (handle DestroyNotify/Disconnected events).","Ensure resize logic does not call render with a 0-width or 0-height pixmap; guard sizes before create_pixmap.","Update/verify cairo and druid-shell versions; cairo-level set_drawable failures can stem from cairo/xcb binding bugs.","Reproduce under Xvfb/xtrace to inspect whether the pixmap creation itself failed before set_drawable."],"exampleFix":"// before\nborrow_mut!(self.cairo_surface)?\n    .set_drawable(&drawable, buffers.width as i32, buffers.height as i32)\n    .map_err(|e| anyhow!(\"Failed to update cairo drawable: {}\", e))?;\n// after\nif buffers.width == 0 || buffers.height == 0 {\n    return Ok(()); // skip render for degenerate sizes\n}\nborrow_mut!(self.cairo_surface)?\n    .set_drawable(&drawable, buffers.width as i32, buffers.height as i32)\n    .map_err(|e| anyhow!(\"Failed to update cairo drawable: {}\", e))?;","handlingStrategy":"try-catch","validationCode":"// before triggering a render on X11\nassert!(window.is_visible(), \"window destroyed; cannot update cairo surface\");\nlet (w, h) = window.get_size();\nassert!(w > 0 && h > 0, \"degenerate window size\");","typeGuard":"fn is_surface_usable(bufs: &SurfaceBuffers) -> bool {\n    bufs.width > 0 && bufs.height > 0 && !bufs.pixmaps.is_empty()\n}","tryCatchPattern":"match window.update_cairo_surface() {\n    Ok(()) => {},\n    Err(e) if e.to_string().contains(\"Failed to update cairo drawable\") => {\n        log::error!(\"cairo drawable update failed: {} — checking X connection\", e);\n        if !connection.is_alive() { reconnect_or_exit(); }\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Guard renders against zero-sized windows during resize storms","Handle X11 disconnect/destroy events before rendering","Keep cairo and druid-shell versions in sync","Test under Xvfb/SSH-forwarded displays before shipping"],"tags":["x11","cairo","rendering","druid-shell"],"backgroundTag":"render-surface-update-failed","analyzedSha":"0f8b1195e4e073f9597f2865299c3d18f8e4005f","analyzedAt":"2026-09-10T14:27:34.582Z","contentChangedAt":"2026-09-10T14:27:34.582Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}