{"record":{"id":"825d7b1f78f6f318","repo":"Hmbown/CodeWhale","slug":"persistent-service-id-lost-its-process-id","errorCode":null,"errorMessage":"Persistent service {id} lost its process id","messagePattern":"Persistent service (.+?) lost its process id","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":2912,"sourceCode":"                .is_none()\n            {\n                return Err(anyhow!(\n                    \"Persistent service {id} has no releasable process id\"\n                ));\n            }\n        }\n\n        let mut receipts = Vec::with_capacity(ids.len());\n        for id in ids {\n            let mut shell = self\n                .processes\n                .remove(&id)\n                .ok_or_else(|| anyhow!(\"Persistent service {id} disappeared during commit\"))?;\n            let pid = shell\n                .child\n                .as_ref()\n                .and_then(ShellChild::process_id)\n                .ok_or_else(|| anyhow!(\"Persistent service {id} lost its process id\"))?;\n            unregister_pending_persistent_process_group(pid);\n            shell.ownership = ShellOwnership::Released;\n            shell.stdin = None;\n            shell.heavy_permit.take();\n            shell.work_lifecycle = None;\n            receipts.push(PersistentServiceReceipt {\n                task_id: id,\n                pid,\n                process_group_id: pid,\n                ownership: \"external\".to_string(),\n            });\n        }\n        Ok(receipts)\n    }\n\n    /// Kill only services waiting for a successful exec ownership transfer.\n    /// Ordinary background jobs retain their existing manager lifetime.\n    pub fn abort_persistent_services(&mut self) {","sourceCodeStart":2894,"sourceCodeEnd":2930,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/shell.rs#L2894-L2930","documentation":"After removing a service from the map, commit re-reads the child's pid to unregister the pending persistent process group and fill the `PersistentServiceReceipt`. The pid existed during the validation loop, so this error means it disappeared between the two loops — the child was reaped mid-commit. The handoff aborts because the process-group registration could not be cleaned up for that pid.","triggerScenarios":"The child handle of a PersistPending shell losing its process id between commit's validation pass and its removal pass (reaping or handle teardown interleaved with commit).","commonSituations":"Same shape as the other mid-commit invariants: concurrent manager mutation or reaping; rare in single-threaded production use.","solutions":["Treat as an internal bug: capture the task_id and reproduction steps","Ensure nothing reaps or mutates manager children while commit runs","After the failure, check for a lingering registered persistent process group for that service"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match manager.commit_persistent_services() {\n    Ok(receipts) => receipts,\n    Err(err) if err.to_string().contains(\"lost its process id\") => {\n        // pid vanished between commit loops: check for a lingering registered\n        // persistent process group, then report as an internal bug\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Prevent concurrent reaping or mutation of manager children during commit","Verify no orphaned pending-persistent process group remains after this failure","Capture the task_id when reporting","Keep commit single-threaded with respect to the whole manager"],"tags":["persistent-services","process-pid","invariant","unix","rust"],"backgroundTag":"child-process-pid-unavailable","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}