{"record":{"id":"215473903b7bfcaf","repo":"BigPizzaV3/CodexPlusPlus","slug":"loopback-port-guard-lock-is-already-held","errorCode":null,"errorMessage":"loopback port guard lock is already held","messagePattern":"loopback port guard lock is already held","errorType":"exception","errorClass":"std::io::Error (WouldBlock)","httpStatus":null,"severity":"warning","filePath":"crates/codex-plus-core/src/ports.rs","lineNumber":249,"sourceCode":"}\n\nfn acquire_lock_guard(port: u16, state_dir: &Path) -> std::io::Result<(File, PathBuf)> {\n    let dir = state_dir.join(\"locks\");\n    std::fs::create_dir_all(&dir)?;\n    let path = dir.join(format!(\"loopback-port-{port}.lock\"));\n    let file = File::options()\n        .read(true)\n        .write(true)\n        .create(true)\n        .truncate(false)\n        .open(&path)?;\n    file.try_lock_exclusive().map_err(normalize_lock_error)?;\n    Ok((file, path))\n}\n\nfn normalize_lock_error(error: std::io::Error) -> std::io::Error {\n    match error.raw_os_error() {\n        Some(33) => std::io::Error::new(\n            std::io::ErrorKind::WouldBlock,\n            \"loopback port guard lock is already held\",\n        ),\n        _ => error,\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::sync::{Mutex, MutexGuard};\n\n    static GUARD_PORT_ENV_LOCK: Mutex<()> = Mutex::new(());\n\n    #[test]\n    fn resilient_guard_holds_lock_and_listener_when_requested_port_is_available() {\n        let temp = tempfile::tempdir().unwrap();\n        let port = find_available_loopback_port();","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/ports.rs#L231-L267","documentation":"The loopback port guard serializes access with an exclusive file lock (try_lock_exclusive). When the underlying OS error is errno 33 (EWOULDBLOCK/EAGAIN — lock held by another process), normalize_lock_error converts it into io::ErrorKind::WouldBlock with this message so callers can recognize contention instead of a generic failure.","triggerScenarios":"Two processes (or two app instances) concurrently attempt to acquire the loopback port guard lock file; the second caller's try_lock_exclusive fails with errno 33 and is normalized to this error.","commonSituations":"Launching a second manager instance while one is running; a crashed process left the lock file but the OS released the lock (less common); CI/test parallelism running two apply flows against the same port-guard file; NFS mounts where lock semantics misbehave.","solutions":["Wait and retry acquiring the guard until the other holder releases it (WouldBlock is transient)","Ensure only one instance of the app/manager runs at a time","On stale-lock suspicion, verify the holder process is gone (the OS lock dies with the process), then retry","Wrap acquisition in a retry-with-backoff loop matching on ErrorKind::WouldBlock"],"exampleFix":"// before\nlet guard = acquire_port_guard()?; // fails when held\n// after\nlet guard = loop {\n    match acquire_port_guard() {\n        Ok(g) => break g,\n        Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {\n            std::thread::sleep(std::time::Duration::from_millis(200));\n        }\n        Err(e) => return Err(e),\n    }\n};","handlingStrategy":"retry","validationCode":"// probe the guard before doing work\nlet probe = std::fs::OpenOptions::new().write(true).open(&lock_path)?;\nlet free = probe.try_lock_exclusive().is_ok();","typeGuard":"const isLockContention = (e: NodeJS.ErrnoException): boolean => e.code === 'EWOULDBLOCK' || e.code === 'EAGAIN';","tryCatchPattern":"match acquire_port_guard() {\n    Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => retry_with_backoff(10, Duration::from_millis(200)),\n    other => other,\n}","preventionTips":["Enforce single-instance behavior in the app (e.g. a startup instance lock)","Always acquire the guard with a bounded retry loop instead of failing fast","Never hold the port guard across long-running operations"],"tags":["locking","concurrency","file-lock","port-guard"],"backgroundTag":"address-already-in-use","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}