{"record":{"id":"170c52500b6f03bb","repo":"Hmbown/CodeWhale","slug":"osc-52-clipboard-fallback-requires-a-terminal","errorCode":null,"errorMessage":"OSC 52 clipboard fallback requires a terminal","messagePattern":"OSC 52 clipboard fallback requires a terminal","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/tui/clipboard.rs","lineNumber":479,"sourceCode":"\n            #[cfg(target_os = \"windows\")]\n            if write_text_with_set_clipboard(text).is_ok() {\n                return Ok(());\n            }\n\n            self.enqueue_terminal_write(text)\n                .map_err(|err| anyhow::anyhow!(\"Clipboard unavailable: {err}\"))\n        }\n    }\n\n    #[cfg(not(test))]\n    fn enqueue_terminal_write(&mut self, text: &str) -> Result<()> {\n        if !self.terminal_context.in_tmux {\n            if text.len() > OSC52_MAX_BYTES {\n                bail!(\"selection is too large for OSC 52 clipboard fallback\");\n            }\n            if !io::stdout().is_terminal() {\n                bail!(\"OSC 52 clipboard fallback requires a terminal\");\n            }\n        }\n\n        if self.terminal_writer.is_none() {\n            self.terminal_writer = Some(TerminalClipboardWriter::spawn()?);\n        }\n        self.terminal_writer\n            .as_ref()\n            .expect(\"terminal clipboard writer initialized\")\n            .enqueue(text, self.terminal_context.in_tmux)\n    }\n\n    /// Return one completed background terminal clipboard write, if available.\n    ///\n    /// Successes are intentionally quiet because callers already show their\n    /// normal copy receipt. Failures are drained by the event loop and replace\n    /// that optimistic receipt with an actionable error.\n    pub(crate) fn poll_write_completion(&self) -> Option<TerminalClipboardWriteCompletion> {","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tui/clipboard.rs#L461-L497","documentation":"The OSC 52 fallback can only write to a terminal, and enqueue_terminal_write detected that stdout is not a TTY while also not being inside tmux (the tmux path routes through load-buffer instead of stdout). This guard prevents writing clipboard escape sequences into a pipe or file.","triggerScenarios":"Invoking the copy action when Codewhale's stdout is piped, redirected, or detached: running under a wrapper that captures output, in tests, or from a daemonized context without tmux.","commonSituations":"Running the TUI through output-capturing wrappers, script(1), or CI; launching from a launcher that redirects stdout; clipboard request arriving after the terminal was detached.","solutions":["Run the TUI with stdout attached to a real terminal (no piping/redirect) when clipboard fallback is needed.","Prefer tmux: inside a tmux session the copy uses load-buffer -w and does not require stdout to be a TTY.","Install a system clipboard utility so the OSC 52 fallback is never selected."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"use std::io::IsTerminal;\n\nfn osc52_write_possible(in_tmux: bool) -> bool {\n    in_tmux || std::io::stdout().is_terminal()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not pipe or redirect the TUI's stdout when clipboard support matters.","Detect headless contexts (CI, wrappers) and disable clipboard features explicitly.","Prefer tmux or a native clipboard helper in captured-output environments."],"tags":["clipboard","osc52","terminal","tty","rust"],"backgroundTag":"clipboard-write-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}