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
- Find and stop the process currently holding the volume (lsof/fuser on the file), then retry.
- Retry with backoff if the other holder is expected to release soon (this is a WouldBlock error, so retrying is meaningful).
- Ensure only one instance per volume: use distinct volume files or a process supervisor.
- 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
- Run a single process per volume file; use a supervisor/lockfile for singletons.
- Detect WouldBlock specifically and retry with backoff instead of crashing.
- Sweep for orphaned processes after crashes (lsof on the volume path).
- Give each concurrent worker its own volume file.
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
- Astrid volume path has no file name
- durable capsule package
- remove durable capsule package
- shutdown stage gateway.lifecycle_fence: a successor gateway…
- AlreadyExists
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)