{"record":{"id":"05958e757782b3c4","repo":"Hmbown/CodeWhale","slug":"another-pet-owner-is-running","errorCode":null,"errorMessage":"Another pet owner is running","messagePattern":"Another pet owner is running","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/owner.rs","lineNumber":301,"sourceCode":"// startup line and stop reason are the operator's only output.\n#[allow(clippy::print_stdout, clippy::print_stderr)]\npub fn serve(root: PathBuf, requested_port: u16) -> anyhow::Result<()> {\n    std::fs::create_dir_all(&root)?;\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::{MetadataExt, PermissionsExt};\n        let metadata = std::fs::symlink_metadata(&root)?;\n        if !metadata.is_dir() || metadata.uid() != unsafe { libc::geteuid() } {\n            anyhow::bail!(\"Pet directory must belong to this user and cannot be a link\");\n        }\n        std::fs::set_permissions(&root, std::fs::Permissions::from_mode(0o700))?;\n    }\n    let lock_path = WorkspaceFile::open(&root, Path::new(\"owner.lock\"), true)?;\n    let original = lock_path.open_update(true, false)?;\n    let mut lifetime = fd_lock::RwLock::new(original.try_clone()?);\n    let _guard = lifetime\n        .try_write()\n        .map_err(|_| anyhow::anyhow!(\"Another pet owner is running\"))?;\n    let mut store = Store::at(&root)?;\n    let previous = store.load()?;\n    let mut saved = if let Some(text) = previous {\n        let value: Saved = serde_json::from_str(&text)?;\n        if value.version != 1\n            || uuid::Uuid::parse_str(&value.identity).is_err()\n            || value.token.len() != 64\n            || !value.token.bytes().all(|b| b.is_ascii_hexdigit())\n            || !valid_source(&value.source)\n            || value.clients.len() > MAX_CLIENTS\n            || !value.appearance.valid()\n        {\n            anyhow::bail!(\"Invalid shared habitat; the existing file was kept\");\n        }\n        value\n    } else {\n        Saved {\n            version: 1,","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/owner.rs#L283-L319","documentation":"serve() takes an exclusive advisory file lock (fd_lock::RwLock::try_write) on owner.lock inside the shared habitat root. If another pet_watch owner process already holds it, try_write fails and this error is returned instead of starting a second owner.","triggerScenarios":"Calling serve() when another pet owner process is alive and holds owner.lock — e.g. a previous instance still running, a crashed instance whose lock file persists while its process still lives, or launching a second owner from another terminal.","commonSituations":"Starting the TUI twice in the same workspace; a stale hung owner from a prior session; container restart where the old owner never exited.","solutions":["Find and terminate the running pet owner process before starting another one","Wait for the existing owner to release the lock (exit or finish)","Verify no orphaned owner survives (ps / container list) and clean it up; the lock is held via a live fd, so removing the file alone will not help"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check whether another owner holds the lock before calling serve()\nlet probe = std::fs::OpenOptions::new().write(true).open(root.join(\"owner.lock\"));\nlet can_take = probe.map(|f| fd_lock::RwLock::new(f).try_write().is_ok());","typeGuard":null,"tryCatchPattern":"match owner::serve(&root) {\n    Err(e) if e.to_string().contains(\"Another pet owner is running\") => {\n        eprintln!(\"Owner already running; attaching to it instead.\");\n    }\n    r => r?,\n}","preventionTips":["Single-instance-check (pid file or lock probe) before launching serve","Shut down the previous TUI instance cleanly so the lock fd is released","In containers, ensure the previous owner process exits on restart"],"tags":["file-lock","single-instance","process-conflict"],"backgroundTag":"file-already-exists","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}