{"record":{"id":"0542a336476bc2cd","repo":"facebook/flow","slug":"lock-file-is-already-held","errorCode":null,"errorMessage":"lock file is already held","messagePattern":"lock file is already held","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"rust_port/crates/flow_server/src/standalone.rs","lineNumber":842,"sourceCode":"\n        match outcome {\n            RecheckOutcome::Ok => {}\n        }\n    }\n}\n\nfn acquire_lock(lock_path: &str) -> std::io::Result<std::fs::File> {\n    if let Some(parent) = Path::new(lock_path).parent() {\n        std::fs::create_dir_all(parent)?;\n    }\n    let file = std::fs::OpenOptions::new()\n        .write(true)\n        .create(true)\n        .truncate(false)\n        .open(lock_path)?;\n    match file.try_lock() {\n        Ok(()) => Ok(file),\n        Err(std::fs::TryLockError::WouldBlock) => Err(std::io::Error::new(\n            std::io::ErrorKind::AlreadyExists,\n            \"lock file is already held\",\n        )),\n        Err(err) => Err(err.into()),\n    }\n}\n\nstruct LockGuard {\n    lock_path: String,\n    socket_path: String,\n    pids_path: String,\n}\n\nimpl Drop for LockGuard {\n    fn drop(&mut self) {\n        let _ = std::fs::remove_file(&self.pids_path);\n        let _ = std::fs::remove_file(&self.socket_path);\n        let _ = std::fs::remove_file(&self.lock_path);","sourceCodeStart":824,"sourceCodeEnd":860,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_server/src/standalone.rs#L824-L860","documentation":"The standalone Flow server enforces single-instance semantics at startup: acquire_lock creates the lock file (and its parent dirs) and takes an exclusive OS-level flock via try_lock. WouldBlock — another process holds the lock — maps to ErrorKind::AlreadyExists \"lock file is already held\". The holder's LockGuard removes the lock, socket, and pids files when it exits cleanly, so this error means a live (or leaked-fd) owner still exists.","triggerScenarios":"Starting a second standalone server with the same lock path while the first is alive; a previous server left running in the background (editor integration, earlier terminal); an orphaned process whose fd still holds the flock after its parent session died.","commonSituations":"Forgetting a server started earlier; a test harness leaving servers behind; a hung server that never released the lock; flock semantics keeping the lock alive while any duplicated fd exists.","solutions":["Find the running instance for that root (process list for the standalone binary, or the pids file next to the lock) and stop it through its normal stop path, then start again.","If no live process holds it, delete the stale lock file and restart.","If you genuinely want a second server, give it a different root/lock path."],"exampleFix":"# before: second start while the first server is alive\nflow standalone ... # -> lock file is already held\n\n# after: stop or reuse the existing instance first\npgrep -af 'flow.*standalone'   # find the live holder\nkill <pid>                     # or use the provided stop command\nrm -f /path/to/flow.lock       # only if no process holds it\nflow standalone ...","handlingStrategy":"validation","validationCode":"use std::fs::OpenOptions;\n\n// Before starting a server, check whether another live instance owns the lock.\nfn lock_is_held(path: &str) -> bool {\n    OpenOptions::new().write(true).create(true).open(path)\n        .map(|f| f.try_lock().is_err()) // fs4::FileExt on std's File\n        .unwrap_or(false)\n}","typeGuard":"fn is_lock_held(e: &std::io::Error) -> bool {\n    e.kind() == std::io::ErrorKind::AlreadyExists && e.to_string().contains(\"already held\")\n}","tryCatchPattern":"On AlreadyExists 'lock file is already held', do not retry in a loop: look up the running instance (pids file next to the lock, process list) and either connect to it or stop it, then start. Only remove the lock file manually after confirming no process holds it.","preventionTips":["Always stop servers through their stop path so LockGuard cleans lock/socket/pids files.","In test harnesses, use unique per-run lock paths to avoid collisions.","After crashes, sweep stale lock/socket files before restarting."],"tags":["server","lock-file","single-instance","flock","rust"],"backgroundTag":"single-instance-lock","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}