{"record":{"id":"fc00b1f1e4f962b9","repo":"can1357/oh-my-pi","slug":"error","errorCode":null,"errorMessage":"{}","messagePattern":"\\{\\}","errorType":"exception","errorClass":"CatError","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/cat.rs","lineNumber":70,"sourceCode":"\tfn increment(&mut self) {\n\t\tfast_inc_one(&mut self.buf, &mut self.num_start, self.num_end);\n\t\tself.print_start = self.print_start.min(self.num_start);\n\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 {","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/cat.rs#L52-L88","documentation":"`CatError::Io` wraps any `std::io::Error` raised while the `cat` builtin reads input files or writes output. The Display formatter calls `strip_errno`, which prints the OS error's message with the ` (os error N)` suffix removed, matching coreutils-style output (e.g. `No such file or directory`). It exists so `cat` presents clean, conventional messages instead of Rust's raw io::Error text.","triggerScenarios":"`cat missing-file` (ENOENT via `File::open`/`metadata`); permission-denied reads; read/write failures mid-copy on regular files, sockets, or devices — any io::Error that is not BrokenPipe and does not map to a dedicated CatError variant in `From<io::Error>`.","commonSituations":"Typos in filenames passed to cat; reading files without permission; cat-ing special files (/dev entries) whose open fails; disk or pipe write failures during concatenation.","solutions":["Read the stripped message (e.g. 'No such file or directory' / 'Permission denied') and fix the path or permissions accordingly.","Verify each argument exists with `ls -l` before cat-ing in scripts.","For special files, confirm the device/socket is present and accessible."],"exampleFix":"// before\n$ cat /path/to/confg.txt\ncat: /path/to/confg.txt: No such file or directory\n// after\n$ cat /path/to/config.txt","handlingStrategy":"try-catch","validationCode":"// shell: verify operands are readable regular files before cat-ing\nfor f in \"$@\"; do\n  if [ ! -f \"$f\" ]; then echo \"not a file: $f\" >&2; continue; fi\n  [ -r \"$f\" ] || { echo \"not readable: $f\" >&2; continue; }\ndone\ncat -- \"$@\"","typeGuard":"// Rust caller: match the wrapped io::Error kind\nif let CatError::Io(io) = &err {\n    if io.kind() == std::io::ErrorKind::NotFound {\n        eprintln!(\"missing input file\");\n    }\n}","tryCatchPattern":"// shell: cat each operand independently so one failure doesn't stop the rest\nfor f in \"$@\"; do\n  cat -- \"$f\" || echo \"failed on $f\" >&2\ndone","preventionTips":["Test -f and -r on each operand before concatenation.","Quote all paths to survive spaces/special characters.","In scripts, prefer explicit error handling per file over a single cat of many operands."],"tags":["shell","builtin","io","file-not-found"],"backgroundTag":"io-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}