astrid-runtime/astrid · error

private path has no file name

Error message

private path has no file name: {}

What it means

After resolving the parent, verify_no_redirects_unix extracts the final component with path.file_name() to open it with O_NOFOLLOW. Paths ending in "..", the root, or otherwise lacking a final component return None, producing io::ErrorKind::InvalidInput.

Solutions

  1. Normalize the path (drop or resolve ".." components, e.g. via lexical normalization or canonicalize the parent) before calling.
  2. Pass a path whose final component is a real entry name.
  3. Validate user-supplied paths to reject trailing ".." components.

Example fix

// before
let p = Path::new("/home/user/.astrid/..");
verify_no_redirects(p)?;
// after
let p = Path::new("/home/user/.astrid/..").canonicalize()?; // -> /home/user
verify_no_redirects(&p)?;
Defensive patterns

Strategy: validation

Validate before calling

if path.file_name().is_none() {
    return Err(format!("path {} has no final component", path.display()));
}

Type guard

fn has_file_name(p: &std::path::Path) -> bool { p.file_name().is_some() }

Try / catch

match verify_no_redirects(&path) {
    Err(e) if e.kind() == io::ErrorKind::InvalidInput => eprintln!("normalize the path (trailing '..' or root): {e}"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling verify_no_redirects with a path ending in ".." (e.g. /home/user/.astrid/..) or another path with no resolvable file-name component.

Common situations: Paths built by string concatenation with ".." segments; user-supplied config paths not normalized; code that joins a directory with ".." expecting it to resolve.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/42d461a0f1772a54. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-core/src/platform_fs.rs:438

fn verify_no_redirects_unix(path: &Path) -> io::Result<()> {
    use nix::fcntl::{OFlag, openat};
    use nix::sys::stat::Mode;

    match std::fs::symlink_metadata(path) {
        Ok(metadata) if metadata.file_type().is_symlink() => Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("private path is redirected: {}", path.display()),
        )),
        Ok(metadata) if metadata.is_dir() => open_directory_no_follow_unix(path).map(drop),
        Ok(_) => {
            let parent = path.parent().ok_or_else(|| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("private path has no parent: {}", path.display()),
                )
            })?;
            let name = path.file_name().ok_or_else(|| {
                io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("private path has no file name: {}", path.display()),
                )
            })?;
            let directory = open_directory_no_follow_unix(parent)?;
            let flags = OFlag::O_RDONLY | OFlag::O_NOFOLLOW | OFlag::O_CLOEXEC | OFlag::O_NONBLOCK;
            openat(&directory, name, flags, Mode::empty())
                .map(std::fs::File::from)
                .map(drop)
                .map_err(nix_io_error)
        },
        Err(error) if error.kind() == io::ErrorKind::NotFound => {
            open_directory_no_follow_unix(path).map(drop)
        },
        Err(error) => Err(error),
    }
}

View on GitHub (pinned to affd8760f4)