Hmbown/CodeWhale · error
OSC 52 clipboard fallback requires a terminal
Error message
OSC 52 clipboard fallback requires a terminal
What it means
The OSC 52 clipboard fallback works by writing an escape sequence to stdout, which only reaches the terminal's clipboard when stdout is an interactive TTY. `enqueue_terminal_write` checks `io::stdout().is_terminal()` (outside tmux) and fails with this error when stdout is redirected or not a terminal, because the sequence would otherwise be written into a file or pipe.
Solutions
- Run in an interactive terminal so stdout is a TTY
- Run inside tmux, where this check is skipped and tmux handles clipboard integration
- Force a system clipboard helper path (wl-copy/xclip/pbcopy) instead of the OSC 52 terminal fallback
- Capture and report the error instead of failing the whole operation when clipboard is best-effort
Example fix
// before
let output = Command::new("codewhale").arg("copy").output()?; // stdout not a TTY
// after
let output = Command::new("codewhale").arg("copy").stdout(Stdio::inherit()).spawn()?; // or run in a TTY Defensive patterns
Strategy: validation
Validate before calling
use std::io::IsTerminal;
if !std::io::stdout().is_terminal() {
// skip terminal clipboard write or use a helper-based backend
} Type guard
fn can_write_osc52() -> bool {
use std::io::IsTerminal;
std::io::stdout().is_terminal()
} Try / catch
match clipboard.write_text(text) {
Err(e) if e.to_string().contains("requires a terminal") => {
eprintln!("clipboard skipped: no TTY on stdout");
}
other => other?,
} Prevention
- Never rely on OSC 52 clipboard writes when stdout may be redirected or piped
- Detect non-interactive environments (CI, daemons) and disable terminal clipboard early
- Prefer system clipboard helpers in scripted contexts
- Run inside tmux where the terminal-TTY requirement is lifted
When it happens
Trigger: Calling `write_text` outside tmux when stdout is redirected (e.g. `codewhale ... > log.txt`), piped to another process, or running in an environment with no controlling TTY (CI, daemon, non-interactive SSH without a terminal).
Common situations: CI jobs trying to copy to the clipboard; running the tool under a process manager or IDE that captures stdout; remote SSH sessions without `-t`; output redirected to a file.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- selection is too large for OSC 52 clipboard fallback
- another terminal clipboard write is still queued
- Clipboard unavailable
- Codewhale TUI could not verify foreground terminal ownership
- Codewhale TUI requires an interactive terminal (stdin and…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/170c52500b6f03bb.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tui/clipboard.rs:634
#[cfg(target_os = "windows")]
if write_text_with_set_clipboard(text).is_ok() {
return Ok(());
}
self.enqueue_terminal_write(text)
.map_err(|err| anyhow::anyhow!("Clipboard unavailable: {err}"))
}
}
#[cfg(not(test))]
fn enqueue_terminal_write(&mut self, text: &str) -> Result<()> {
if !self.terminal_context.in_tmux {
if text.len() > OSC52_MAX_BYTES {
bail!("selection is too large for OSC 52 clipboard fallback");
}
if !io::stdout().is_terminal() {
bail!("OSC 52 clipboard fallback requires a terminal");
}
}
if self.terminal_writer.is_none() {
self.terminal_writer = Some(TerminalClipboardWriter::spawn()?);
}
self.terminal_writer
.as_ref()
.expect("terminal clipboard writer initialized")
.enqueue(text, self.terminal_context.in_tmux)
}
/// Return one completed background terminal clipboard write, if available.
///
/// Successes are intentionally quiet because callers already show their
/// normal copy receipt. Failures are drained by the event loop and replace
/// that optimistic receipt with an actionable error.
pub(crate) fn poll_write_completion(&self) -> Option<TerminalClipboardWriteCompletion> {View on GitHub (pinned to 73e0f67d83)