linebender/druid · error
Failed to produce a text context
Error message
Failed to produce a text context
What it means
The web backend's WindowHandle::text() returns a PietText handle built from the window's rendering context, which is held via a Weak reference. If the underlying window (WebWindow) has already been dropped (its canvas/context gone), upgrade() returns None and the code panics instead of returning an error. It means you called text() on a window handle that no longer points to a live window.
Solutions
- Only call text() while the window is guaranteed alive; obtain the PietText handle during setup (in the window's init/connect handlers) and store it.
- Don't hold WindowHandle clones in globals or JS callbacks that can outlive the window; tie their lifetime to the window's lifetime.
- Restructure async/deferred work to go through the shell's scheduled callbacks (idle/timers owned by the window) so they are cancelled when the window dies.
- If you need delayed text access, keep the parent Druid app state alive and fetch text through the app context rather than a raw handle.
Example fix
// before: using a stashed handle later
let handle = window_handle.clone();
on_timeout(move || {
let piet_text = handle.text(); // panics if window dropped
});
// after: capture the text handle while the window is alive
let piet_text = window.text();
on_timeout(move || {
let piet_text = piet_text.clone(); // safe, independent of window lifetime
}); Defensive patterns
Strategy: try-catch
Validate before calling
// web backend: check window liveness before use (no public API, so track it yourself) let window_alive = !dropped_windows.contains(&window_id);
Type guard
function isWindowAlive(handle: WindowHandleRef): boolean {
return handle.weak.upgrade().is_some(); // mirror of Weak::upgrade check
} Try / catch
let text = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| window.text()))
.map_err(|_| anyhow!("window already dropped; text() unavailable"))?; Prevention
- Acquire PietText during window setup and reuse it instead of calling text() later.
- Never store WindowHandle in globals, JS closures, or timers that can outlive the window.
- Route deferred work through window-owned idle/timer callbacks so it is cancelled on close.
- On wasm, remember the canvas removal drops the window — clear stored handles on close.
When it happens
Trigger: Calling window.text() (druid-shell web backend, window.rs:589) after the window was closed/dropped — e.g. holding a WindowHandle past window close, or calling text() from an async callback/timer after the canvas was removed from the DOM.
Common situations: WebAssembly apps using druid-shell directly that stash a WindowHandle in a global or JS-side closure and use it later; background tasks or requestAnimationFrame callbacks outliving a dropped window; forgetting that the web backend's window is garbage-collected with its canvas.
Related errors
- Failed to request animation frame
- Failed to call setTimeout with a callback
- request_animation_frame failed
- Unwrap named called on unnamed FieldIdent
- Application is already running
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/6d145252cc92b223.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/web/window.rs:589
s.invalid.borrow_mut().add_rect(rect);
}
self.render_soon();
}
pub fn invalidate(&self) {
if let Some(s) = self.0.upgrade() {
s.invalid
.borrow_mut()
.add_rect(s.area.get().size_dp().to_rect());
}
self.render_soon();
}
pub fn text(&self) -> PietText {
let s = self
.0
.upgrade()
.unwrap_or_else(|| panic!("Failed to produce a text context"));
PietText::new(s.context.clone())
}
pub fn add_text_field(&self) -> TextFieldToken {
TextFieldToken::next()
}
pub fn remove_text_field(&self, token: TextFieldToken) {
if let Some(state) = self.0.upgrade() {
if state.active_text_input.get() == Some(token) {
state.active_text_input.set(None);
}
}
}
pub fn set_focused_text_field(&self, active_field: Option<TextFieldToken>) {
if let Some(state) = self.0.upgrade() {View on GitHub (pinned to 0f8b1195e4)