linebender/druid · error
release_input_lock was called on a WinHandler that did not…
Error message
release_input_lock was called on a WinHandler that did not expect text input.
What it means
This panic is the default (no-op) implementation of `WinHandler::release_input_lock` in druid-shell's window.rs. The shell calls it when a platform text-input path finishes editing and tries to release the lock acquired via `acquire_input_lock`, but the handler's default implementation did not expect any text input. It mirrors the acquire-side panic and signals the same contract violation on the release path.
Solutions
- Override `release_input_lock` in your WinHandler to unlock the state corresponding to the given TextFieldToken
- Also override `acquire_input_lock` — if release fired, acquire likely did too; implement both symmetrically
- Track which tokens were acquired so release can be matched correctly (map from token to handler state)
Example fix
// before
impl WinHandler for MyAppHandler {} // panicking defaults
// after
impl WinHandler for MyAppHandler {
fn release_input_lock(&mut self, token: TextFieldToken) {
if let Some(field) = self.text_field_for(token) {
field.unlock_input();
}
}
} Defensive patterns
Strategy: type-guard
Validate before calling
// Only release locks for tokens the handler actually acquired:
fn has_acquired(handler: &MyHandler, token: TextFieldToken) -> bool {
handler.acquired_tokens.contains(&token)
}
Type guard
fn handles_text_input(handler: &dyn WinHandler) -> bool {
!handler.text_widgets().is_empty()
}
Try / catch
// Avoid relying on panic recovery; guard instead:
if handler.handles_text_input() {
handler.release_input_lock(token);
} else {
tracing::warn!("release_input_lock on non-text handler ignored");
}
Prevention
- Always override release_input_lock together with acquire_input_lock
- Track acquired tokens in a set and only release known tokens
- Audit WinHandler impls after upgrading druid-shell for new text-input trait methods
- Exercise IME/candidate-window paths in tests so release is covered
When it happens
Trigger: The backend completes a text-editing session and calls `release_input_lock(token)` on a WinHandler using the default trait implementation that never registered text fields or overrode the lock methods.
Common situations: Custom WinHandlers with default trait impls receiving IME events; a text field whose acquire was handled elsewhere but release lands on the default; version skew where druid-shell added the lock API after the handler was written.
Related errors
- acquire_input_lock was called on a WinHandler that did not…
- The main thread status has already been claimed by thread
- No path received for filename
- More than one path received for single selection
- There is no globally active Application
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/13fccfa63f240ccf.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/window.rs:670
///
/// For more information, see [the text input documentation](crate::text).
///
/// [`release_input_lock`]: WinHandler::release_input_lock
#[allow(unused_variables)]
fn acquire_input_lock(
&mut self,
token: TextFieldToken,
mutable: bool,
) -> Box<dyn InputHandler> {
panic!("acquire_input_lock was called on a WinHandler that did not expect text input.")
}
/// Release a lock previously acquired by [`acquire_input_lock`].
///
/// [`acquire_input_lock`]: WinHandler::acquire_input_lock
#[allow(unused_variables)]
fn release_input_lock(&mut self, token: TextFieldToken) {
panic!("release_input_lock was called on a WinHandler that did not expect text input.")
}
/// Called on a mouse wheel event.
///
/// The polarity is the amount to be added to the scroll position,
/// in other words the opposite of the direction the content should
/// move on scrolling. This polarity is consistent with the
/// deltaX and deltaY values in a web [WheelEvent].
///
/// [WheelEvent]: https://w3c.github.io/uievents/#event-type-wheel
#[allow(unused_variables)]
fn wheel(&mut self, event: &MouseEvent) {}
/// Called when a platform-defined zoom gesture occurs (such as pinching
/// on the trackpad).
#[allow(unused_variables)]
fn zoom(&mut self, delta: f64) {}
View on GitHub (pinned to 0f8b1195e4)