{"record":{"id":"c0dba9fb337916a6","repo":"Hmbown/CodeWhale","slug":"persistent-service-id-has-no-releasable-process","errorCode":null,"errorMessage":"Persistent service {id} has no releasable process id","messagePattern":"Persistent service (.+?) has no releasable process id","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/shell.rs","lineNumber":2896,"sourceCode":"            let shell = self\n                .processes\n                .get_mut(id)\n                .ok_or_else(|| anyhow!(\"Persistent service {id} disappeared before commit\"))?;\n            shell.poll();\n            if shell.status != ShellStatus::Running {\n                return Err(anyhow!(\n                    \"Persistent service {id} exited before ownership transfer (status {:?}, exit code {:?})\",\n                    shell.status,\n                    shell.exit_code\n                ));\n            }\n            if shell\n                .child\n                .as_ref()\n                .and_then(ShellChild::process_id)\n                .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;","sourceCodeStart":2878,"sourceCodeEnd":2914,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/tools/shell.rs#L2878-L2914","documentation":"Committing persistent ownership requires each service to expose a releasable OS process id from its `ShellChild` handle. This error means `shell.child` is gone or carries no pid while the job is still flagged PersistPending and Running — the child handle was dropped or reaped, so ownership cannot transfer and the pending process-group registration cannot be cleaned. It is a state-invariant failure inside the manager, not an input error.","triggerScenarios":"A PersistPending BackgroundShell whose `child` is None (hand-constructed values, test seeds) or whose child was reaped before the commit pass while status still reads Running.","commonSituations":"Test fixtures inserting shells without children; platform-specific reaping paths that clear the pid without flipping the status.","solutions":["Only create persist:true jobs through the normal spawn path so a child handle always exists","Do not hand-construct BackgroundShell values outside ShellManager","Treat occurrence as an internal bug: report the task_id with a reproduction","After the failure, verify no orphaned process group remains (the pending registration was not unregistered)"],"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(\"no releasable process id\") => {\n        // internal invariant: child handle gone while flagged Running — report as a bug\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Create persist:true jobs only through the normal spawn path","Never hand-construct BackgroundShell values for production use","Check for orphaned registered process groups after this failure","Report occurrences with the task_id and reproduction steps"],"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-14T05:17:10.506Z"}