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

  1. Check each path component with metadata() before descending
  2. Remove the trailing path components below the file
  3. 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

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


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