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

  1. Test the helper standalone: `echo hi | pbcopy` (macOS) or `echo hi | powershell.exe -NoProfile -Command Set-Clipboard` (Windows)
  2. For PowerShell failures check execution policy: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
  3. Treat EPIPE as 'helper refused' and let the caller fall through to the OSC 52/tmux path (the built-in behavior)
  4. 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

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


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/baca2d9b39c33267. Report an issue: GitHub.