linebender/druid · error
Window::render - piet finish failed
Error message
Window::render - piet finish failed: {} What it means
Raised in `Window::render` on X11 when `piet_ctx.finish()` fails after the client's `paint` handler ran successfully. `finish` flushes the cairo/piet drawing commands into the surface; an error here means the recorded draw operations could not be completed (e.g. an invalid render target or a draw call that poisoned the context). The message embeds the underlying piet error.
Solutions
- Inspect the wrapped piet error text to find which draw operation failed; fix the offending draw call in the widget's paint method.
- Verify the window/pixmap is valid (non-zero size, not destroyed) before painting.
- Ensure piet-cairo and druid-shell versions are compatible; cairo backend bugs can surface at finish().
- Wrap custom paint code defensively so it cannot leave the context in an error state (e.g. skip drawing with invalid Image/Text layouts).
Defensive patterns
Strategy: try-catch
Validate before calling
// in custom widget paint code, before drawing
if !ctx.is_target_valid() || ctx.size().is_empty() { return; }
for img in &images_to_draw { assert!(img.is_valid()); } Try / catch
match result_of_render {
Err(e) if e.to_string().contains("piet finish failed") => {
log::error!("piet finish failed: {} — reviewing paint code for invalid draw calls", e);
schedule_full_repaint();
},
other => other,
} Prevention
- Never draw with stale or unloaded piet Image/Text resources
- Skip painting when the widget/target size is zero
- Avoid issuing draw calls that can poison the piet context
- Pin compatible piet-cairo and druid-shell versions
When it happens
Trigger: During `render` (invoked from `redraw_now` or `handle_complete_notify`), the handler borrow succeeded so `handler.paint` ran and `piet_ctx.finish()` returned Err inside the `with_handler_and_dont_check_the_other_borrows` closure.
Common situations: Client `paint` code issuing draws that put the piet/cairo context into an error state (e.g. invalid image sources, zero-size targets), or the underlying X surface becoming invalid (window destroyed, resized to 0).
Related errors
- Failed to update cairo drawable
- after rendering, no pixmap to present
- unexpected mouse wheel button
- Application is already running
- Application state already borrowed
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/dd76ad9aebcc0c77.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/x11/window.rs:802
let rect = rect.to_px(scale).round();
cairo_ctx.rectangle(rect.x0, rect.y0, rect.width(), rect.height());
}
cairo_ctx.clip();
cairo_ctx.scale(scale.x(), scale.y());
let mut piet_ctx = Piet::new(&cairo_ctx);
// We need to be careful with earlier returns here, because piet_ctx
// can panic if it isn't finish()ed. Also, we want to reset cairo's clip
// even on error.
//
// Note that we're borrowing the surface while calling the handler. This is ok, because
// we don't return control to the system or re-borrow the surface from any code that
// the client can call.
let result = self.with_handler_and_dont_check_the_other_borrows(|handler| {
handler.paint(&mut piet_ctx, &invalid);
piet_ctx
.finish()
.map_err(|e| anyhow!("Window::render - piet finish failed: {}", e))
});
let err = match result {
None => {
// The handler borrow failed, so finish didn't get called.
piet_ctx
.finish()
.map_err(|e| anyhow!("Window::render - piet finish failed: {}", e))
}
Some(e) => {
// Finish might have errored, in which case we want to propagate it.
e
}
};
cairo_ctx.reset_clip();
err?;
}
View on GitHub (pinned to 0f8b1195e4)