Hmbown/CodeWhale · error · anyhow::Error
Failed to write to {label}: {e}
Error message
Failed to write to {label}: {e} What it means
Writing the payload to the clipboard helper's piped stdin failed in write_text_with_stdin_command. In practice this is almost always BrokenPipe (EPIPE): the helper (pbcopy or powershell.exe) exited before consuming the input, closing the pipe while write_all was still pushing bytes. It can also fire when the helper is killed mid-write.
Source
Thrown at crates/tui/src/tui/clipboard.rs:541
#[cfg(all(any(target_os = "macos", target_os = "windows"), not(test)))]
fn write_text_with_stdin_command(
program: &str,
args: &[&str],
text: &str,
label: &str,
) -> Result<()> {
let mut child = Command::new(program)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(|e| anyhow::anyhow!("Failed to run {label}: {e}"))?;
if let Some(mut stdin) = child.stdin.take() {
stdin
.write_all(text.as_bytes())
.map_err(|e| anyhow::anyhow!("Failed to write to {label}: {e}"))?;
}
let _ = std::thread::Builder::new()
.name("clipboard-wait".to_string())
.spawn(move || {
let _ = child.wait();
});
Ok(())
}
#[cfg(all(target_os = "linux", not(target_env = "ohos"), not(test)))]
fn write_text_with_wlcopy(text: &str) -> Result<()> {
write_text_with_wlcopy_using_argv("wl-copy", text)
}
#[cfg(all(target_os = "linux", not(target_env = "ohos"), not(test)))]
fn read_text_with_wlpaste() -> Result<String> {
read_text_with_wlpaste_using_argv("wl-paste")
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Test the helper standalone: `echo hi | pbcopy` (macOS) or `echo hi | powershell.exe -NoProfile -Command Set-Clipboard` (Windows)
- For PowerShell failures check execution policy: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
- Treat EPIPE as 'helper refused' and let the caller fall through to the OSC 52/tmux path (the built-in behavior)
- Keep payloads moderate so the helper is not overrun before it starts reading
Defensive patterns
Strategy: fallback
Validate before calling
// Rust - confirm the helper accepts input before routing the real payload
fn helper_accepts_stdin(program: &str, args: &[&str]) -> bool {
std::process::Command::new(program)
.args(args)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.and_then(|mut c| { if let Some(mut s) = c.stdin.take() { s.write_all(b"")?; } c.wait() })
.map(|st| st.success())
.unwrap_or(false)
} Try / catch
match write_text_with_stdin_command(prog, args, text, label) {
Err(e) if e.to_string().contains("Broken pipe") || e.to_string().contains("broken pipe") => {
try_terminal_client_fallback(); // helper refused; do not retry the same helper
}
other => other,
} Prevention
- Verify helpers standalone (`echo hi | pbcopy`) after policy/sandbox changes
- Keep payloads moderate so helpers can start reading before the pipe fills
- Watch PowerShell execution-policy changes that kill Set-Clipboard at startup
When it happens
Trigger: child.stdin.write_all returns Err(BrokenPipe): powershell.exe aborts immediately (execution policy, constrained language mode, profile error), pbcopy crashes on startup under sandbox restrictions, or the helper is OOM-killed while the TUI writes a large payload.
Common situations: PowerShell execution policy blocking Set-Clipboard; macOS Seatbelt denying pbcopy; very large payloads racing a fast-exiting helper.
Related errors
- Failed to run {label}: {e}
- Failed to write to {program}: {e}
- API key input is unexpectedly large
- interactive key entry requires a terminal; use `--api-key-st
- credential handoff could not write to stdout
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/baca2d9b39c33267.
Report an issue: GitHub.