astrid-runtime/astrid · error

Astrid volume is not a regular file

Error message

Astrid volume is not a regular file

What it means

After opening the volume file, open() stats it and requires metadata.is_file(). Paths that resolve to directories, FIFOs, devices, or reparse-point/symlink targets that are not regular files are rejected with InvalidData. This protects the locking and fixed-offset I/O model, which only works on regular files.

Source

Thrown at crates/astrid-storage/src/volume/hosted/open.rs:91

        let swap_guard = open_lock.lock();
        reclaim::recover_artifacts(&path)?;
        let mut options = OpenOptions::new();
        options.read(true).write(true).create(true);
        #[cfg(unix)]
        {
            use std::os::unix::fs::OpenOptionsExt as _;
            options.mode(0o600).custom_flags(libc::O_NOFOLLOW);
        }
        #[cfg(windows)]
        {
            use std::os::windows::fs::OpenOptionsExt as _;
            use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT;
            options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);
        }
        let mut file = options.open(&path)?;
        let metadata = file.metadata()?;
        if !metadata.is_file() {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "Astrid volume is not a regular file",
            ));
        }
        file.try_lock_exclusive().map_err(|error| {
            if error.kind() == io::ErrorKind::WouldBlock {
                io::Error::new(io::ErrorKind::WouldBlock, "Astrid volume is already open")
            } else {
                error
            }
        })?;
        let length = file.metadata()?.len();
        if length == 0 {
            file.write_all(&VOLUME_MAGIC)?;
            file.sync_all()?;
        } else {
            let mut magic = [0_u8; VOLUME_MAGIC.len()];
            file.read_exact(&mut magic)?;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Verify the path points at a regular file: run `file <path>` or check it is not a directory/symlink.
  2. Fix the configured volume path to reference the actual volume file.
  3. If a symlink is intentional, point the config at the real regular-file target.
  4. Recreate the volume with the library's create path if the file was replaced by something else.

Example fix

// before
let volume = HostedVolume::open("/data/volumes")?; // a directory
// after
let volume = HostedVolume::open("/data/volumes/volume.astrid")?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_regular_file(path: &Path) -> std::io::Result<bool> {
    Ok(std::fs::metadata(path)?.is_file())
}

Try / catch

match HostedVolume::open(&path) {
    Err(e) if e.kind() == io::ErrorKind::InvalidData
        && e.to_string().contains("not a regular file") => {
        eprintln!("{path:?} is not a regular file; fix VOLUME_PATH");
    }
    other => { /* ... */ }
}

Prevention

When it happens

Trigger: Opening a volume path that is a directory; opening a special file (fifo/socket/device); opening through a symlink or Windows reparse point that resolves to a non-regular target (note FILE_FLAG_OPEN_REPARSE_POINT is used on Windows).

Common situations: Misconfiguration pointing VOLUME_PATH at a directory or a mount point; a broken provisioning step replaced the volume file with a symlink to something else; tmpfs/pipe-based test fixtures.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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