linebender/druid · error · std::io::Error (TimedOut)
Timeout while waiting for selection owner to reply
Error message
Timeout while waiting for selection owner to reply
What it means
The X11 clipboard transfer implementation asks the current selection owner for clipboard data and then waits for the reply event on the X connection. If the owner does not respond before the deadline, wait_for_event_with_deadline returns an io::Error with ErrorKind::TimedOut and this message, surfaced (via do_transfer_impl) as a ClipboardError::Io.
Solutions
- Retry the clipboard read after a short delay; transient owner hangs often clear.
- Check that the source application holding the clipboard is still responsive; re-copy from it.
- If it recurs with one specific app, work around by using an intermediate clipboard (e.g. `xclip -selection clipboard -o` from shell) or a clipboard manager.
- Upgrade druid/druid-shell; X11 clipboard handling has had timeout and error-handling fixes.
- Handle the TimedOut io error in app code around clipboard get_string calls so a failed paste does not panic or hang.
Example fix
// before
let text = window.get_clipboard_string().unwrap();
// after
let text = match window.get_clipboard_string() {
Ok(t) => t,
Err(e) if matches!(e, druid_shell::Error::ShellError(_)) || e.to_string().contains("Timeout") => {
tracing::warn!("clipboard transfer timed out");
String::new()
}
Err(e) => return Err(e.into()),
}; Defensive patterns
Strategy: retry
Try / catch
match window.get_clipboard_string() {
Ok(text) => text,
Err(err) if err.to_string().contains("Timeout while waiting for selection owner") => {
// retry once, then fall back
window.get_clipboard_string().unwrap_or_default()
}
Err(err) => return Err(err.into()),
} Prevention
- Keep clipboard source applications responsive; re-copy before pasting if the source was closed.
- Wrap clipboard reads in retry-with-backoff helpers.
- On flaky X11 setups prefer clipboard managers (e.g. clipman, parcellite) as intermediaries.
- Avoid pasting from apps in the middle of teardown.
When it happens
Trigger: Calling clipboard get/set_string (or paste) while the selection owner (another X client holding the clipboard) is hung, dead, or slow to respond; race where the owning window closed between claiming the selection and the data request; extremely large transfers exceeding the deadline.
Common situations: Copy/paste from a frozen or misbehaving application under X11; clipboard managers or window-manager shutdown mid-transfer; running druid apps in remote/VNC X sessions with high latency; pasting immediately after closing the source app.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- unexpected wayland event
- Invalid screen num
- Couldn't get visual from screen
- invalid screen num
- No window with id
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/6435c15f87480e52.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/x11/clipboard.rs:664
/// Wait for an X11 event or return a timeout error if the given deadline is in the past.
fn wait_for_event_with_deadline(
conn: &XCBConnection,
deadline: Instant,
) -> Result<Event, ConnectionError> {
use nix::poll::{poll, PollFd, PollFlags};
use std::os::raw::c_int;
use std::os::unix::io::AsRawFd;
loop {
// Is there already an event?
if let Some(event) = conn.poll_for_event()? {
return Ok(event);
}
// Are we past the deadline?
let now = Instant::now();
if deadline <= now {
return Err(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"Timeout while waiting for selection owner to reply",
)
.into());
}
// Use poll() to wait for the socket to become readable.
let mut poll_fds = [PollFd::new(conn.as_raw_fd(), PollFlags::POLLIN)];
let poll_timeout = c_int::try_from(deadline.duration_since(now).as_millis())
.unwrap_or(c_int::MAX - 1)
// The above rounds down, but we don't want to wake up to early, so add one
.saturating_add(1);
// Wait for the socket to be readable via poll() and try again
match poll(&mut poll_fds, poll_timeout) {
Ok(_) => {}
Err(nix::errno::Errno::EINTR) => {}
Err(e) => return Err(std::io::Error::from(e).into()),View on GitHub (pinned to 0f8b1195e4)