astrid-runtime/astrid · error

Astrid volume is already open

Error message

Astrid volume is already open

What it means

open() takes an exclusive advisory lock (try_lock_exclusive) on the volume file so only one process can have a volume open at a time. When the lock fails with WouldBlock — another process already holds it — it is translated to this WouldBlock error meaning "Astrid volume is already open". Any other lock error is passed through unchanged.

Solutions

  1. Find and stop the process currently holding the volume (lsof/fuser on the file), then retry.
  2. Retry with backoff if the other holder is expected to release soon (this is a WouldBlock error, so retrying is meaningful).
  3. Ensure only one instance per volume: use distinct volume files or a process supervisor.
  4. Check for orphaned processes from a previous crashed run.

Example fix

// before
let volume = HostedVolume::open(&path)?; // fails if already open
// after
let volume = loop {
    match HostedVolume::open(&path) {
        Ok(v) => break v,
        Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
            std::thread::sleep(Duration::from_millis(250));
        }
        Err(e) => return Err(e),
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// Advisory: check for other holders of the file before opening
// lsof / fuser <volume-path> from ops tooling
let already_locked = std::fs::OpenOptions::new()
    .read(true).open(&path)?
    .try_lock_exclusive().is_err();

Try / catch

match HostedVolume::open(&path) {
    Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
        // volume in use elsewhere — wait, then retry
        std::thread::sleep(Duration::from_millis(500));
        /* retry */
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling open() on a volume file that another process (or another instance in the same program) already opened and locked; a stale holder process that never exited; NFS setups where advisory locks behave unexpectedly.

Common situations: Double-starting a service without a supervisor singleton; a previous crashed-but-alive worker still holding the lock; running two dev instances against the same volume file; CI jobs sharing a mounted volume.

Related errors


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

Appendix: source

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

            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)?;
            if magic != VOLUME_MAGIC {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "invalid Astrid volume header",
                ));
            }
        }

View on GitHub (pinned to affd8760f4)