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
- Point the API at an actual regular file path, not a directory or special file.
- If a directory exists at that path, move or rename it and let the library create the file.
- Check the path with `stat -c '%F' <path>` (or `ls -l`) to confirm it is a regular file before retrying.
- 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
- Point private-path config at concrete file paths, never directories
- Check `stat -c '%F'` on the path before wiring it up
- Do not place FIFOs/sockets in private state directories
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
- private file is not owned by the current user: {}
- legacy capsule directory is not a regular directory: {}
- legacy capsule entry is not a regular directory: {}
- capsule materialization target is redirected or not a direct
- mountpoint must be absolute
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/5e2f3927d11f9193.
Report an issue: GitHub.