can1357/oh-my-pi · error · LsError
cannot access {}: Not a directory
Error message
cannot access {}: Not a directory What it means
A specialized ls error mapping io::ErrorKind::NotADirectory to the GNU-style message 'cannot access <path>: Not a directory'. It fires when ls attempts to stat or traverse a path component that is a regular file but is used as a directory (e.g. a trailing path component below a file).
Source
Thrown at crates/pi-builtins/src/ls.rs:3683
}
}
use colors::StyleManager;
pub use config::{Config, options};
use config::{Dereference, Files, Sort, options::QUOTING_STYLE};
use dired::DiredOutput;
pub use display::Format;
use display::{display_items, display_size, should_display, show_dir_name};
#[derive(Error, Debug)]
enum LsError {
#[error("invalid line width: '{0}'")]
InvalidLineWidth(String),
#[error("general io error: {0}")]
IOError(#[from] std::io::Error),
#[error("{}", match .1.kind() {
ErrorKind::NotADirectory => format!("cannot access {}: Not a directory", .0.quote()),
ErrorKind::NotFound => format!("cannot access {}: No such file or directory", .0.quote()),
ErrorKind::PermissionDenied => match .1.raw_os_error().unwrap_or(1) {
1 => format!("cannot access {}: Operation not permitted", .0.quote()),
_ => if *.3 {
format!("cannot open directory {}: Permission denied", .0.quote())
} else {
format!("cannot open file {}: Permission denied", .0.quote())
},
},
_ => if 9 == .1.raw_os_error().unwrap_or(1) {
format!("cannot open directory {}: Bad file descriptor", .0.quote())
} else {
format!("unknown io error: {}, '{:?}'", .0.quote(), .1)
},
})]
IOErrorContext(PathBuf, std::io::Error, bool, bool),
View on GitHub (pinned to 9690622007)
Solutions
- Check each path component with metadata() before descending
- Remove the trailing path components below the file
- Verify the intended path — the parent should be a directory
Example fix
// before
ls(&host, &["notes.txt/details"])?;
// after
if std::path::Path::new("notes.txt").is_dir() {
ls(&host, &["notes.txt/details"])?;
} else {
ls(&host, &["notes.txt"])?;
} Defensive patterns
Strategy: validation
Validate before calling
fn ensure_dir(p: &Path) -> std::io::Result<()> {
let md = std::fs::metadata(p)?;
if !md.is_dir() {
return Err(std::io::Error::new(std::io::ErrorKind::NotADirectory, format!("{}: not a directory", p.display())));
}
Ok( )
} Type guard
fn is_dir_path(p: &Path) -> bool {
std::fs::metadata(p).map(|m| m.is_dir()).unwrap_or(false)
} Try / catch
match ls(&host, &[path]) {
Err(e) if e.to_string().contains("Not a directory") => {
eprintln!("{path} is a file; listing the file itself instead");
ls(&host, &[path.rsplit_once('/').map(|(p, _)| p).unwrap_or(".")])?;
}
other => other?,
} Prevention
- Verify intermediate path components are directories before appending subpaths
- Avoid trailing slashes on file paths
- Build paths from validated directory handles rather than string concatenation
When it happens
Trigger: `ls file.txt/subpath` where file.txt is a regular file; `ls file.txt/` with a trailing slash on a non-directory.
Common situations: Scripts building paths from variables where an intermediate component turns out to be a file; typos appending /subdir to a file path.
Related errors
- failed to access {0}: Not a directory
- 1
- Not a file: ${shortenPath(resolved)}
- File not found: ${path}
- File not found: ${inputPath}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f7e9c5242bc838df.
Report an issue: GitHub.