can1357/oh-my-pi · info · CatError
broken pipe
Error message
broken pipe
What it means
`CatError::BrokenPipe` is produced by `From<io::Error>` when the error kind is `ErrorKind::BrokenPipe`, meaning the downstream reader closed the pipe `cat` was writing to (classic `EPIPE`). Per the doc comment, this ends the copy quietly — it is the normal mechanism behind `cat big | head`, so it is treated as a distinct, benign variant rather than a generic I/O failure.
Source
Thrown at crates/pi-builtins/src/cat.rs:73
}
#[inline]
fn to_str(&self) -> &[u8] {
&self.buf[self.print_start..]
}
fn write(&self, writer: &mut impl Write) -> io::Result<()> {
writer.write_all(self.to_str())
}
}
#[derive(Error, Debug)]
enum CatError {
/// Wrapper around `io::Error`.
#[error("{}", strip_errno(.0))]
Io(io::Error),
/// The downstream reader closed its pipe; this ends the copy quietly.
#[error("broken pipe")]
BrokenPipe,
/// Unknown file type; it is not a regular file, socket, or known device.
#[error("unknown filetype: {ft_debug}")]
UnknownFiletype { ft_debug: String },
#[error("Is a directory")]
IsDirectory,
#[cfg(unix)]
#[error("No such device or address")]
NoSuchDeviceOrAddress,
#[error("Too many levels of symbolic links")]
TooManySymlinks,
}
impl From<io::Error> for CatError {
fn from(error: io::Error) -> Self {
if error.kind() == ErrorKind::BrokenPipe {
Self::BrokenPipe
} else {View on GitHub (pinned to 9690622007)
Solutions
- No action needed if you intentionally truncated with head/similar — this is expected behavior.
- If unintended, check that the downstream command in the pipeline runs without crashing (run it alone).
- Use `cat file > out` or redirect to a file if you need the full copy regardless of the reader.
Example fix
// expected, no fix needed: $ cat big.log | head -n 10 // if unintended, isolate the failing consumer: $ cat big.log | consumer-cmd # check consumer-cmd's own error
Defensive patterns
Strategy: try-catch
Validate before calling
// shell: avoid premature reader death by bounding input up front cat big.log | head -n 10 # expected: head closes the pipe early # or fully consume when you need everything: cat big.log > trimmed_copy.txt
Type guard
// Rust caller: treat BrokenPipe as benign
match err {
CatError::BrokenPipe => { /* downstream closed; exit quietly */ }
other => eprintln!("cat failed: {other}"),
} Try / catch
// shell: suppress the diagnostic when truncation is intentional cat big.log 2>/dev/null | head -n 10 || true
Prevention
- Expect EPIPE whenever piping into head/tail/less; don't treat it as a real failure.
- Check pipeline consumers for early-exit bugs when broken pipes appear unexpectedly.
- Redirect to a file when the full output must be produced regardless of the reader.
When it happens
Trigger: `cat file | head -n 5` (or `less`/any pager exited early); writing into a pipe whose reader process has already exited; `cat file | some-command` where some-command crashes or closes stdout early.
Common situations: Deliberately truncating output with head/tail; a downstream command in a pipeline failing and taking the pipe with it; SIGPIPE-style termination in long pipelines.
Understand the failure class
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
Related errors
- unknown function: {0}
- unknown key binding function: {0}
- unimplemented: {0}
- I/O error occurred
- transparent (brush_parser::BindingParseError)
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/21d72b542bac2359.
Report an issue: GitHub.