Hmbown/CodeWhale · error · anyhow::Error
Persistent service {id} has no releasable process id
Error message
Persistent service {id} has no releasable process id What it means
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.
Source
Thrown at crates/tui/src/tools/shell.rs:2896
let shell = self
.processes
.get_mut(id)
.ok_or_else(|| anyhow!("Persistent service {id} disappeared before commit"))?;
shell.poll();
if shell.status != ShellStatus::Running {
return Err(anyhow!(
"Persistent service {id} exited before ownership transfer (status {:?}, exit code {:?})",
shell.status,
shell.exit_code
));
}
if shell
.child
.as_ref()
.and_then(ShellChild::process_id)
.is_none()
{
return Err(anyhow!(
"Persistent service {id} has no releasable process id"
));
}
}
let mut receipts = Vec::with_capacity(ids.len());
for id in ids {
let mut shell = self
.processes
.remove(&id)
.ok_or_else(|| anyhow!("Persistent service {id} disappeared during commit"))?;
let pid = shell
.child
.as_ref()
.and_then(ShellChild::process_id)
.ok_or_else(|| anyhow!("Persistent service {id} lost its process id"))?;
unregister_pending_persistent_process_group(pid);
shell.ownership = ShellOwnership::Released;View on GitHub (pinned to 0c42157ee5)
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)
Defensive patterns
Strategy: try-catch
Try / catch
match manager.commit_persistent_services() {
Ok(receipts) => receipts,
Err(err) if err.to_string().contains("no releasable process id") => {
// internal invariant: child handle gone while flagged Running — report as a bug
}
Err(err) => return Err(err),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Test fixtures inserting shells without children; platform-specific reaping paths that clear the pid without flipping the status.
Related errors
- Persistent service {id} lost its process id
- Persistent service {id} disappeared before commit
- Persistent service {id} disappeared during commit
- Persistent service {id} exited before ownership transfer (st
- snapshot dir has no parent
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/c0dba9fb337916a6.
Report an issue: GitHub.