astrid-runtime/astrid · error

private file has no parent

Error message

private file has no parent: {}

What it means

open_file_no_follow_unix needs both a parent directory and a final file name to open the file relative to its parent via openat (for symlink protection). It throws this when path.parent() returns None, which happens for bare-root paths like "/" that cannot name a file.

Solutions

  1. Pass the full path to the file including its parent directory, not "/" or a bare root.
  2. Check the configured value; assign the concrete file name (e.g. /home/me/.astrid/credentials).
  3. Guard caller code to reject paths without a parent before invoking the API.
  4. If the value comes from user input or env, validate it names a file before use.

Example fix

// before
restrict_private_file(Path::new("/"))?;
// after
restrict_private_file(&Path::new("/home/me/.astrid").join("credentials"))?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_parent(path: &std::path::Path) -> bool { path.parent().is_some() }

Type guard

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

Try / catch

assert!(path.parent().is_some(), "private path must include a parent: {}", path.display());
restrict_private_file(path)?;

Prevention

When it happens

Trigger: Calling restrict_private_file or validate_private_file with a Path that has no parent component, such as "/" or an empty/normalized root path.

Common situations: A config variable that should hold a file path accidentally holds a mount root or "/"; path-joining code that stripped all components; programmatic construction passing Path::new("").

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

    }
}

#[cfg(target_os = "macos")]
fn absolute_command_path(path: &Path) -> io::Result<PathBuf> {
    if path.is_absolute() {
        Ok(path.to_path_buf())
    } else {
        Ok(std::env::current_dir()?.join(path))
    }
}

#[cfg(unix)]
fn open_file_no_follow_unix(path: &Path) -> io::Result<std::fs::File> {
    use nix::fcntl::{OFlag, openat};
    use nix::sys::stat::Mode;

    let parent = path.parent().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            format!("private file has no parent: {}", path.display()),
        )
    })?;
    let name = path.file_name().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            format!("private file has no name: {}", path.display()),
        )
    })?;
    let directory = open_directory_no_follow_unix(parent)?;
    let flags = OFlag::O_RDONLY | OFlag::O_NOFOLLOW | OFlag::O_CLOEXEC;
    openat(&directory, name, flags, Mode::from_bits_truncate(0o600))
        .map(std::fs::File::from)
        .map_err(nix_io_error)
}

#[cfg(unix)]

View on GitHub (pinned to affd8760f4)