can1357/oh-my-pi · error · CatError
{}
Error message
{} What it means
`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.
Source
Thrown at crates/pi-builtins/src/cat.rs:70
fn increment(&mut self) {
fast_inc_one(&mut self.buf, &mut self.num_start, self.num_end);
self.print_start = self.print_start.min(self.num_start);
}
#[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 {View on GitHub (pinned to 9690622007)
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.
Example fix
// before $ cat /path/to/confg.txt cat: /path/to/confg.txt: No such file or directory // after $ cat /path/to/config.txt
Defensive patterns
Strategy: try-catch
Validate before calling
// shell: verify operands are readable regular files before cat-ing
for f in "$@"; do
if [ ! -f "$f" ]; then echo "not a file: $f" >&2; continue; fi
[ -r "$f" ] || { echo "not readable: $f" >&2; continue; }
done
cat -- "$@" Type guard
// Rust caller: match the wrapped io::Error kind
if let CatError::Io(io) = &err {
if io.kind() == std::io::ErrorKind::NotFound {
eprintln!("missing input file");
}
} Try / catch
// shell: cat each operand independently so one failure doesn't stop the rest for f in "$@"; do cat -- "$f" || echo "failed on $f" >&2 done
Prevention
- 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.
When it happens
Trigger: `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>`.
Common situations: 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.
Understand the failure class
Background: Rust io::Error: what ErrorKind::NotFound, PermissionDenied, and InvalidData actually mean — from real OS failures to library fail-closed refusals — this error's family across 12 libraries.
Related errors
- I/O error occurred
- unknown function: {0}
- unknown key binding function: {0}
- unimplemented: {0}
- transparent (brush_parser::BindingParseError)
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/fc00b1f1e4f962b9.
Report an issue: GitHub.