rtk-ai/rtk · error

Failed to read stdin: {}

Error message

Failed to read stdin: {}

What it means

Filtered pipe mode reads ALL of stdin into a String via read_to_string, which requires the stream to be valid UTF-8; any IO error or invalid byte sequence is reported as `Failed to read stdin: {e}`. The read is capped at RAW_CAP+1 bytes (10 MiB) so oversize input fails on the explicit size check, not here — this error is about readability/IO, not size.

Source

Thrown at src/cmds/system/pipe_cmd.rs:257

    std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| filter_fn(input)))
        .unwrap_or_else(|_| {
            eprintln!("[rtk] warning: filter panicked — passing through raw output");
            input.to_string()
        })
}

pub fn run(filter_name: Option<&str>, passthrough: bool) -> Result<()> {
    if passthrough {
        std::io::copy(&mut std::io::stdin(), &mut std::io::stdout())
            .map_err(|e| anyhow::anyhow!("Failed to relay stdin: {}", e))?;
        return Ok(());
    }

    let mut buf = String::new();
    std::io::stdin()
        .take((RAW_CAP + 1) as u64)
        .read_to_string(&mut buf)
        .map_err(|e| anyhow::anyhow!("Failed to read stdin: {}", e))?;
    if buf.len() > RAW_CAP {
        anyhow::bail!("stdin exceeds {} byte limit", RAW_CAP);
    }

    let filter_fn = match filter_name {
        Some(name) => resolve_filter(name).ok_or_else(|| {
            anyhow::anyhow!(
                "Unknown filter '{}'. Available: cargo-test, pytest, go-test, go-build, \
                 tsc, vitest, grep, rg, find, fd, git-log, git-diff, git-status, \
                 log, mypy, ruff-check, ruff-format, prettier, phpunit, pest, \
                 paratest, php-test, ecs, phpstan, pint",
                name
            )
        })?,
        None => auto_detect_filter(&buf),
    };

    let output = apply_filter(filter_fn, &buf);

View on GitHub (pinned to d977e1c316)

Solutions

  1. Switch to byte-safe passthrough: `gzip -dc app.log.gz | rtk pipe --passthrough` (io::copy, no UTF-8 requirement, no cap)
  2. Convert to text before filtering: `gzip -dc app.log.gz | strings | rtk pipe`, or `iconv -f latin1 -t utf8 logs.txt | rtk pipe`
  3. Pre-verify the stream: `iconv -f utf-8 -t utf-8 < input > /dev/null` exits non-zero on invalid UTF-8

Example fix

# before: binary stream -> "Failed to read stdin: stream did not contain valid UTF-8"
gzip -dc app.log.gz | rtk pipe

# after
gzip -dc app.log.gz | rtk pipe --passthrough   # byte-for-byte, no UTF-8 need
gzip -dc app.log.gz | zcat -f | strings | rtk pipe  # or make it textual first
Defensive patterns

Strategy: validation

Validate before calling

bash:
# verify the stream is UTF-8 before handing it to rtk pipe
iconv -f utf-8 -t utf-8 < input > /dev/null 2>&1 || { echo "not UTF-8 — use rtk pipe --passthrough" >&2; exit 2; }
rtk pipe --filter cargo-test < input

Type guard

rust:
fn stdin_is_utf8(buf: &[u8]) -> bool {
    std::str::from_utf8(buf).is_ok()
}

Try / catch

rust:
match pipe_cmd::run(filter_name, false) {
    Err(e) if e.to_string().contains("stream did not contain valid UTF-8")
           || e.to_string().contains("Failed to read stdin") => {
        // binary input: redo as byte-safe passthrough, no UTF-8 requirement
        pipe_cmd::run(None, true)
    }
    r => r,
}

Prevention

When it happens

Trigger: Piping non-UTF-8 bytes into `rtk pipe` or `rtk pipe --filter <name>`: `gzip -dc app.log.gz | rtk pipe` (compressed bytes), tar streams, images/binaries, latin-1 or UTF-16 logs; also a stdin fd that returns an error (closed or bad descriptor).

Common situations: Forgetting that .gz/.zst output is binary until decompressed; Windows-origin logs in UTF-16; locale-mojibake build logs with stray 0x80-0xFF bytes; piping `cat` of a .node/.wasm asset.

Related errors


AI-assisted analysis of rtk-ai/rtk@d977e1c316 (2026-08-16). Data as JSON: /api/errors/229f9a4cee75fd30. Report an issue: GitHub.