{"record":{"id":"21d72b542bac2359","repo":"can1357/oh-my-pi","slug":"broken-pipe","errorCode":null,"errorMessage":"broken pipe","messagePattern":"broken pipe","errorType":"exception","errorClass":"CatError","httpStatus":null,"severity":"info","filePath":"crates/pi-builtins/src/cat.rs","lineNumber":73,"sourceCode":"\t}\n\n\t#[inline]\n\tfn to_str(&self) -> &[u8] {\n\t\t&self.buf[self.print_start..]\n\t}\n\n\tfn write(&self, writer: &mut impl Write) -> io::Result<()> {\n\t\twriter.write_all(self.to_str())\n\t}\n}\n\n#[derive(Error, Debug)]\nenum CatError {\n\t/// Wrapper around `io::Error`.\n\t#[error(\"{}\", strip_errno(.0))]\n\tIo(io::Error),\n\t/// The downstream reader closed its pipe; this ends the copy quietly.\n\t#[error(\"broken pipe\")]\n\tBrokenPipe,\n\t/// Unknown file type; it is not a regular file, socket, or known device.\n\t#[error(\"unknown filetype: {ft_debug}\")]\n\tUnknownFiletype { ft_debug: String },\n\t#[error(\"Is a directory\")]\n\tIsDirectory,\n\t#[cfg(unix)]\n\t#[error(\"No such device or address\")]\n\tNoSuchDeviceOrAddress,\n\t#[error(\"Too many levels of symbolic links\")]\n\tTooManySymlinks,\n}\n\nimpl From<io::Error> for CatError {\n\tfn from(error: io::Error) -> Self {\n\t\tif error.kind() == ErrorKind::BrokenPipe {\n\t\t\tSelf::BrokenPipe\n\t\t} else {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/cat.rs#L55-L91","documentation":"`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.","triggerScenarios":"`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.","commonSituations":"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.","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."],"exampleFix":"// expected, no fix needed:\n$ cat big.log | head -n 10\n// if unintended, isolate the failing consumer:\n$ cat big.log | consumer-cmd   # check consumer-cmd's own error","handlingStrategy":"try-catch","validationCode":"// shell: avoid premature reader death by bounding input up front\ncat big.log | head -n 10    # expected: head closes the pipe early\n# or fully consume when you need everything:\ncat big.log > trimmed_copy.txt","typeGuard":"// Rust caller: treat BrokenPipe as benign\nmatch err {\n    CatError::BrokenPipe => { /* downstream closed; exit quietly */ }\n    other => eprintln!(\"cat failed: {other}\"),\n}","tryCatchPattern":"// shell: suppress the diagnostic when truncation is intentional\ncat big.log 2>/dev/null | head -n 10 || true","preventionTips":["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."],"tags":["shell","builtin","pipe","sigpipe"],"backgroundTag":"broken-pipe","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}