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

  1. Retry the clipboard read after a short delay; transient owner hangs often clear.
  2. Check that the source application holding the clipboard is still responsive; re-copy from it.
  3. 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.
  4. Upgrade druid/druid-shell; X11 clipboard handling has had timeout and error-handling fixes.
  5. 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

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.

Related errors


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)