{"record":{"id":"55afa2c1ed5e5484","repo":"BoundaryML/baml","slug":"timed-out-waiting-for-install-lock","errorCode":null,"errorMessage":"timed out waiting for install lock {}","messagePattern":"timed out waiting for install lock (.+?)","errorType":"exception","errorClass":"FetchError","httpStatus":null,"severity":"error","filePath":"baml_language/crates/baml_release/src/lib.rs","lineNumber":254,"sourceCode":"    }\n}\n\nstruct InstallLock {\n    path: PathBuf,\n}\n\nimpl InstallLock {\n    fn acquire(path: PathBuf) -> Result<Self, FetchError> {\n        for _ in 0..60 {\n            match OpenOptions::new().write(true).create_new(true).open(&path) {\n                Ok(_) => return Ok(Self { path }),\n                Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {\n                    thread::sleep(Duration::from_secs(1));\n                }\n                Err(err) => return Err(FetchError::Io(err)),\n            }\n        }\n        Err(FetchError::Io(std::io::Error::new(\n            std::io::ErrorKind::TimedOut,\n            format!(\"timed out waiting for install lock {}\", path.display()),\n        )))\n    }\n}\n\nimpl Drop for InstallLock {\n    fn drop(&mut self) {\n        let _ = fs::remove_file(&self.path);\n    }\n}\n\npub fn manifest_base_url() -> String {\n    std::env::var(\"BAML_MANIFEST_BASE_URL\")\n        .ok()\n        .filter(|value| !value.trim().is_empty())\n        .unwrap_or_else(|| DEFAULT_MANIFEST_BASE_URL.to_string())\n        .trim_end_matches('/')","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/baml_release/src/lib.rs#L236-L272","documentation":"acquire() implements a lockfile-based install mutex: it retries create_new() every second when the lock file already exists. If the lock is still held after the retry budget is exhausted, it returns an io::Error with ErrorKind::TimedOut formatted as \"timed out waiting for install lock {path}\". This means another process (or a stale one) holds the BAML install lock.","triggerScenarios":"Calling acquire (during install/self-update) while another baml process holds the lock file at `path`, or a previous crashed run left the lock file behind so create_new keeps failing with AlreadyExists until timeout.","commonSituations":"Two parallel `baml install`/CI jobs racing; a killed installer that never removed its lock; NFS/containers where stale locks persist; very slow installation exceeding the wait window.","solutions":["Wait for the concurrent baml process to finish, then retry the install.","Delete the stale lock file shown in the message and re-run the command.","Ensure CI runs installs with a per-job lock or serialized steps.","If timeouts recur legitimately, increase the retry budget or remove the lock after checking no live process holds it."],"exampleFix":"// before\nbaml install 0.200.0 &\nbaml install 0.200.0 &   # second one times out on the lock\n// after\nbaml install 0.200.0\nbaml install 0.200.0     # serialize installs, or rm the stale lock first","handlingStrategy":"retry","validationCode":"if lock_path.exists() {\n    eprintln!(\"install lock exists: {} — waiting or removing stale lock\", lock_path.display());\n}","typeGuard":null,"tryCatchPattern":"match acquire_install_lock() {\n    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {\n        if no_live_holder(&lock_path) { std::fs::remove_file(&lock_path)?; retry(); } else { backoff_and_retry(); }\n    }\n    r => r,\n}","preventionTips":["Serialize baml installs in CI (don't run them in parallel jobs).","Clean up lock files in signal/exit handlers so crashes don't leave stale locks.","Monitor for orphaned lock files in shared/NFS mounts."],"tags":["lock","timeout","concurrency","install"],"backgroundTag":"request-timeout","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}