{"record":{"id":"e7cf80c47c61b80b","repo":"Hmbown/CodeWhale","slug":"clipboard-unavailable-err","errorCode":null,"errorMessage":"Clipboard unavailable: {err}","messagePattern":"Clipboard unavailable: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"crates/tui/src/tui/clipboard.rs","lineNumber":435,"sourceCode":"    pub fn write_text(&mut self, text: &str) -> Result<()> {\n        #[cfg(test)]\n        {\n            if let Some(writer) = self.terminal_writer.as_ref() {\n                return writer.enqueue(text, self.terminal_context.in_tmux);\n            }\n            if self.fail_text_writes {\n                bail!(\"test clipboard unavailable\");\n            }\n            self.written_text.push(text.to_string());\n            Ok(())\n        }\n\n        #[cfg(not(test))]\n        {\n            if self.terminal_context.write_order() == ClipboardWriteOrder::TerminalClientOnly {\n                return self\n                    .enqueue_terminal_write(text)\n                    .map_err(|err| anyhow::anyhow!(\"Clipboard unavailable: {err}\"));\n            }\n\n            #[cfg(all(target_os = \"linux\", not(target_env = \"ohos\")))]\n            if write_text_with_wlcopy(text).is_ok() {\n                return Ok(());\n            }\n\n            #[cfg(any(\n                target_os = \"macos\",\n                target_os = \"windows\",\n                all(target_os = \"linux\", not(target_env = \"ohos\"))\n            ))]\n            {\n                self.ensure_clipboard();\n                if let Some(clipboard) = self.clipboard.as_mut()\n                    && clipboard.set_text(text.to_string()).is_ok()\n                {\n                    return Ok(());","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tui/clipboard.rs#L417-L453","documentation":"Thrown by ClipboardHandler::write_text when the detected terminal context forces ClipboardWriteOrder::TerminalClientOnly (an SSH session with no forwarded X11/Wayland display, or CODEWHALE_SSH_CLIPBOARD=terminal). On this path no native clipboard helper is tried; the write goes straight to enqueue_terminal_write (tmux load-buffer or OSC 52), and any failure there is wrapped as 'Clipboard unavailable: {err}'. Typical inner errors: selection larger than OSC52_MAX_BYTES (100 KiB) outside tmux, stdout not a terminal, terminal writer spawn/queue failure, or tmux errors.","triggerScenarios":"write_text() while SSH_CLIENT/SSH_CONNECTION/SSH_TTY is set with no usable DISPLAY/WAYLAND_DISPLAY, and then: (a) not in tmux and text.len() > 100*1024, (b) io::stdout() is not a terminal (piped/redirected), (c) the TerminalClipboardWriter thread failed to spawn or stopped, (d) another terminal write still occupies the single-slot queue ('another terminal clipboard write is still queued'), or (e) inside tmux the `tmux load-buffer -w -` child fails to spawn or exits non-zero.","commonSituations":"SSH into a remote host without X11 forwarding and copying a large transcript; TUI stdout piped or redirected; local terminal with OSC 52 disabled or unsupported (many defaults); rapid consecutive large copies overrunning the one-slot write queue.","solutions":["Copy a smaller selection (under 100 KiB), or run inside tmux where the OSC 52 size cap does not apply","Use an OSC 52-capable terminal (iTerm2, kitty, WezTerm, Alacritty; tmux needs set-clipboard on) and keep stdout attached to the TTY","Forward a display (ssh -X/-Y) or set CODEWHALE_SSH_CLIPBOARD=graphical with a live display so the native clipboard path is used instead","Wait for the queued terminal write to drain before copying again; avoid back-to-back large copies"],"exampleFix":"# before\nssh remote-host        # no -X; copy a 500 KiB selection\n# -> Clipboard unavailable: selection is too large for OSC 52 clipboard fallback\n\n# after\nssh -X remote-host     # forwarded display -> native clipboard path\n# or run inside tmux on the remote host and keep selections under 100 KiB","handlingStrategy":"fallback","validationCode":"// Rust - pre-flight the terminal-client copy path\nuse std::io::IsTerminal;\nconst OSC52_MAX_BYTES: usize = 100 * 1024;\n\nfn terminal_copy_ok(text: &str, in_tmux: bool) -> bool {\n    if !in_tmux {\n        if text.len() > OSC52_MAX_BYTES { return false; }\n        if !std::io::stdout().is_terminal() { return false; }\n    }\n    true\n}","typeGuard":null,"tryCatchPattern":"match clipboard.write_text(text) {\n    Ok(()) => show_copy_receipt(),\n    Err(err) => show_warning(format!(\"Clipboard unavailable: {err:#}\")),\n} // copy is best-effort: never abort the turn on clipboard failure","preventionTips":["Keep copyable selections under 100 KiB when relying on OSC 52","Prefer tmux or an OSC 52-capable terminal for remote sessions","Know the env contract: SSH_TTY set with empty DISPLAY/WAYLAND_DISPLAY forces the terminal-client-only path"],"tags":["clipboard","osc52","tmux","ssh","tui"],"backgroundTag":"clipboard-write-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}