astrid-runtime/astrid · error

private path is not a regular file: {}

Error message

private path is not a regular file: {}

What it means

Astrid enforces that private security-sensitive paths are regular files before restricting their permissions to 0600. This error is thrown by restrict_private_file_unix when fstat on the no-follow-opened path shows the file type bits are not S_IFREG (0o100000), e.g. the path is a directory, device, FIFO, socket, or symlink target of that kind. It is a defensive check so ownership/permission hardening is never applied to non-regular inodes.

Source

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

    }
    if metadata.permissions().mode() & 0o777 != 0o700 {
        return Err(io::Error::new(
            io::ErrorKind::PermissionDenied,
            format!("private directory is not owner-only: {}", path.display()),
        ));
    }
    validate_no_extended_acl(path)?;
    Ok(())
}

#[cfg(unix)]
fn restrict_private_file_unix(path: &Path) -> io::Result<()> {
    use nix::sys::stat::{Mode, fchmod, fstat};

    let file = open_file_no_follow_unix(path)?;
    let metadata = fstat(&file).map_err(nix_io_error)?;
    if metadata.st_mode & 0o170_000 != 0o100_000 {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("private path is not a regular file: {}", path.display()),
        ));
    }
    fchmod(&file, Mode::from_bits_truncate(0o600)).map_err(nix_io_error)?;
    file.sync_all()?;
    drop(file);
    #[cfg(target_os = "macos")]
    remove_extended_acl_macos(path)?;
    validate_private_file_unix(path)
}

#[cfg(unix)]
fn validate_private_file_unix(path: &Path) -> io::Result<()> {
    use nix::sys::stat::fstat;

    let file = open_file_no_follow_unix(path)?;
    let metadata = fstat(&file).map_err(nix_io_error)?;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Point the API at an actual regular file path, not a directory or special file.
  2. If a directory exists at that path, move or rename it and let the library create the file.
  3. Check the path with `stat -c '%F' <path>` (or `ls -l`) to confirm it is a regular file before retrying.
  4. If a device/FIFO was intentionally placed there, remove it; private state must live in a regular file.

Example fix

// before
restrict_private_file(Path::new("/home/me/.astrid"))?; // directory
// after
restrict_private_file(Path::new("/home/me/.astrid/credentials"))?; // regular file
Defensive patterns

Strategy: validation

Validate before calling

use std::os::unix::fs::FileTypeExt;
fn is_regular_file(path: &std::path::Path) -> std::io::Result<bool> {
    Ok(std::fs::symlink_metadata(path)?.file_type().is_file())
}
if !is_regular_file(path)? { return Err(anyhow!("{} must be a regular file", path.display())); }

Type guard

fn is_regular(path: &std::path::Path) -> bool {
    std::fs::symlink_metadata(path).map(|m| m.is_file()).unwrap_or(false)
}

Try / catch

match restrict_private_file(path) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => eprintln!("not a regular file: {e}"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling restrict_private_file (or an API that uses it) with a path that resolves to a directory, character/block device, FIFO, socket, or other non-regular inode. Because the file is opened with O_NOFOLLOW, the fstat reflects the real target of the final component.

Common situations: Pointing a private-file config option at a directory instead of a file; a FIFO or socket exists at the expected path; a leftover mount point or /dev-style device file sits where a token/credential file should be.

Related errors


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