{"record":{"id":"bad2ea4e8522b017","repo":"rtk-ai/rtk","slug":"failed-to-relay-stdin","errorCode":null,"errorMessage":"Failed to relay stdin: {}","messagePattern":"Failed to relay stdin: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmds/system/pipe_cmd.rs","lineNumber":249,"sourceCode":"    identity_filter\n}\n\nfn identity_filter(input: &str) -> String {\n    input.to_string()\n}\n\nfn apply_filter(filter_fn: fn(&str) -> String, input: &str) -> String {\n    std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| filter_fn(input)))\n        .unwrap_or_else(|_| {\n            eprintln!(\"[rtk] warning: filter panicked — passing through raw output\");\n            input.to_string()\n        })\n}\n\npub fn run(filter_name: Option<&str>, passthrough: bool) -> Result<()> {\n    if passthrough {\n        std::io::copy(&mut std::io::stdin(), &mut std::io::stdout())\n            .map_err(|e| anyhow::anyhow!(\"Failed to relay stdin: {}\", e))?;\n        return Ok(());\n    }\n\n    let mut buf = String::new();\n    std::io::stdin()\n        .take((RAW_CAP + 1) as u64)\n        .read_to_string(&mut buf)\n        .map_err(|e| anyhow::anyhow!(\"Failed to read stdin: {}\", e))?;\n    if buf.len() > RAW_CAP {\n        anyhow::bail!(\"stdin exceeds {} byte limit\", RAW_CAP);\n    }\n\n    let filter_fn = match filter_name {\n        Some(name) => resolve_filter(name).ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Unknown filter '{}'. Available: cargo-test, pytest, go-test, go-build, \\\n                 tsc, vitest, grep, rg, find, fd, git-log, git-diff, git-status, \\\n                 log, mypy, ruff-check, ruff-format, prettier, phpunit, pest, \\","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/rtk-ai/rtk/blob/d977e1c31621fe8704e6500ceeb9c7a0de2b6836/src/cmds/system/pipe_cmd.rs#L231-L267","documentation":"In passthrough mode (`rtk pipe --passthrough`), rtk does a byte-for-byte std::io::copy from stdin to stdout; any IO error on either end surfaces as `Failed to relay stdin: {e}`. The dominant cause is EPIPE: the downstream consumer (head, grep -m1, an early-exiting agent reader) closed the pipe before rtk finished writing.","triggerScenarios":"`rtk pipe --passthrough < big.log | head -5`; piping into `grep -m1 pattern` which exits after the first match; a downstream process crashing mid-stream; an upstream producer closing stdin early; SSH session drop mid-relay.","commonSituations":"Unix pipelines that truncate output after rtk (head/sed q/awk exit); agent harnesses that read N bytes then close the fd; piping into tools that exit on first match; flaky remote sessions.","solutions":["Make the downstream consumer read until EOF, or write to a file instead: `rtk pipe --passthrough < in > out.txt`","Move early-exiting consumers (head, grep -m1) upstream of rtk pipe, or replace `cmd | rtk pipe --passthrough | head` with `head input | rtk pipe --passthrough`","If you wrap rtk in code, treat EPIPE/BrokenPipeError as success — the bytes written before close were delivered"],"exampleFix":"# before: head closes the pipe -> \"Failed to relay stdin: Broken pipe\"\nrtk pipe --passthrough < huge.log | head -5\n\n# after: consume to EOF (file) or truncate before rtk\nrtk pipe --passthrough < huge.log > excerpt.txt\nhead -c 1M huge.log | rtk pipe --passthrough","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"rust (wrapping rtk pipe --passthrough):\nuse std::io::ErrorKind;\nmatch std::io::copy(&mut std::io::stdin(), &mut std::io::stdout()) {\n    Err(e) if e.kind() == ErrorKind::BrokenPipe => Ok(()), // consumer done: not an error\n    other => other,\n}\nbash equivalent: `rtk pipe --passthrough < in 2>/dev/null | head -5; test ${PIPESTATUS[0]:-0} -eq 0 || true` — or simply ensure the consumer reads to EOF.","preventionTips":["Never place early-exiting consumers (head, sed q, grep -m1) downstream of rtk pipe","Write to a file when you need the full stream: `rtk pipe --passthrough < in > out.txt`","Treat EPIPE after rtk as success in wrappers — bytes before the close were delivered","Keep truncation upstream: `head -c N input | rtk pipe --passthrough`"],"tags":["stdin","pipe","io","epipe","passthrough"],"backgroundTag":null,"analyzedSha":"d977e1c31621fe8704e6500ceeb9c7a0de2b6836","analyzedAt":"2026-08-16T05:40:46.291Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}