{"record":{"id":"c710a11c0dc8541b","repo":"astrid-runtime/astrid","slug":"astrid-volume-is-already-open","errorCode":null,"errorMessage":"Astrid volume is already open","messagePattern":"Astrid volume is already open","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage/src/volume/hosted/open.rs","lineNumber":98,"sourceCode":"            options.mode(0o600).custom_flags(libc::O_NOFOLLOW);\n        }\n        #[cfg(windows)]\n        {\n            use std::os::windows::fs::OpenOptionsExt as _;\n            use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT;\n            options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);\n        }\n        let mut file = options.open(&path)?;\n        let metadata = file.metadata()?;\n        if !metadata.is_file() {\n            return Err(io::Error::new(\n                io::ErrorKind::InvalidData,\n                \"Astrid volume is not a regular file\",\n            ));\n        }\n        file.try_lock_exclusive().map_err(|error| {\n            if error.kind() == io::ErrorKind::WouldBlock {\n                io::Error::new(io::ErrorKind::WouldBlock, \"Astrid volume is already open\")\n            } else {\n                error\n            }\n        })?;\n        let length = file.metadata()?.len();\n        if length == 0 {\n            file.write_all(&VOLUME_MAGIC)?;\n            file.sync_all()?;\n        } else {\n            let mut magic = [0_u8; VOLUME_MAGIC.len()];\n            file.read_exact(&mut magic)?;\n            if magic != VOLUME_MAGIC {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    \"invalid Astrid volume header\",\n                ));\n            }\n        }","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage/src/volume/hosted/open.rs#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet volume = HostedVolume::open(&path)?; // fails if already open\n// after\nlet volume = loop {\n    match HostedVolume::open(&path) {\n        Ok(v) => break v,\n        Err(e) if e.kind() == io::ErrorKind::WouldBlock => {\n            std::thread::sleep(Duration::from_millis(250));\n        }\n        Err(e) => return Err(e),\n    }\n};","handlingStrategy":"retry","validationCode":"// Advisory: check for other holders of the file before opening\n// lsof / fuser <volume-path> from ops tooling\nlet already_locked = std::fs::OpenOptions::new()\n    .read(true).open(&path)?\n    .try_lock_exclusive().is_err();","typeGuard":null,"tryCatchPattern":"match HostedVolume::open(&path) {\n    Err(e) if e.kind() == io::ErrorKind::WouldBlock => {\n        // volume in use elsewhere — wait, then retry\n        std::thread::sleep(Duration::from_millis(500));\n        /* retry */\n    }\n    other => other,\n}","preventionTips":["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."],"tags":["storage","locking","concurrency","exclusive-access"],"backgroundTag":"address-already-in-use","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}