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

  1. Run in an interactive terminal so stdout is a TTY
  2. Run inside tmux, where this check is skipped and tmux handles clipboard integration
  3. Force a system clipboard helper path (wl-copy/xclip/pbcopy) instead of the OSC 52 terminal fallback
  4. 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

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


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)