can1357/oh-my-pi · error · HeadError

error reading

Error message

error reading {}: {}

What it means

HeadError::Io wraps any io::Error encountered while reading an input file during the head builtin's execution, formatting it as 'error reading <name>: <err>' with the path shell-quoted. It surfaces underlying OS-level read failures (file not found, permission denied, etc.) with the offending file's name attached.

Solutions

  1. Verify the file path exists and is spelled correctly (ls the directory)
  2. Check read permissions on the file (ls -l) and adjust with chmod/chown as appropriate
  3. Inspect the wrapped inner err message for the precise OS-level cause
  4. Handle the error variant in your caller to report or skip unreadable files gracefully

Example fix

// before
head("/var/log/missing.log", 10);
// after
let path = PathBuf::from("/var/log/app.log");
if path.exists() { head(&path, 10); }
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check before invoking head
if !std::path::Path::new(path).is_file() {
	eprintln!("cannot read {path}: not a readable file");
}

Try / catch

match head_result {
	Err(HeadError::Io { name, err }) => eprintln!("skipping {}: {err}", name.display()),
	Err(other) => return Err(other),
	Ok(v) => /* ... */,
}

Prevention

When it happens

Trigger: Calling the head builtin with a path that does not exist, lacks read permission, or where the underlying reader returns an I/O error mid-read (e.g. device errors, broken pipes on special files).

Common situations: Typos in file paths; reading files owned by another user; operating on dangling symlinks; passing directories where files are expected; network/special files that error during read.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/89b0e502545f6a7e. Report an issue: GitHub.

Appendix: source

Thrown at crates/pi-builtins/src/head.rs:987

	#[test]
	fn test_more_lines() {
		let input_reader = std::io::Cursor::new("a\nb\nc\n");
		let output_reader = BufReader::new(take_lines(input_reader, 4, b'\n'));
		let mut iter = output_reader.lines().map(|l| l.unwrap());
		assert_eq!(Some(String::from("a")), iter.next());
		assert_eq!(Some(String::from("b")), iter.next());
		assert_eq!(Some(String::from("c")), iter.next());
		assert_eq!(None, iter.next());
	}
}
}

use take::{copy_all_but_n_bytes, copy_all_but_n_lines, take_lines};

#[derive(Error, Debug)]
enum HeadError {
	/// Wrapper around `io::Error`
	#[error("error reading {}: {}", name.quote(), err)]
	Io { name: PathBuf, err: io::Error },

	#[error("{0}")]
	ParseError(String),

	#[error("number of -bytes or -lines is too large")]
	NumTooLarge(#[from] TryFromIntError),


	#[error("{0}")]
	MatchOption(String),
}

type HeadResult<T> = Result<T, HeadError>;

#[derive(Debug, PartialEq)]
enum Mode {
	FirstLines(u64),

View on GitHub (pinned to 9690622007)