Hmbown/CodeWhale · error · anyhow::Error
Persistent service {id} disappeared during commit
Error message
Persistent service {id} disappeared during commit What it means
The second phase of `commit_persistent_services` removes each validated ID from `processes` to build the receipt; this error fires when the entry vanishes after the validation pass but before its removal. Single-threaded, the loops are back-to-back, so hitting it means the map was mutated inside the commit — an invariant breach, not a usage error.
Source
Thrown at crates/tui/src/tools/shell.rs:2907
}
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;
shell.stdin = None;
shell.heavy_permit.take();
shell.work_lifecycle = None;
receipts.push(PersistentServiceReceipt {
task_id: id,
pid,
process_group_id: pid,
ownership: "external".to_string(),
});
}
Ok(receipts)View on GitHub (pinned to 0c42157ee5)
Solutions
- Serialize all ShellManager access so commit runs to completion without interleaving calls
- Treat as a bug: log the task_id and audit what mutated the map mid-commit
- Re-derive the pending set and retry commit for the surviving services
Defensive patterns
Strategy: try-catch
Try / catch
match manager.commit_persistent_services() {
Ok(receipts) => receipts,
Err(err) if err.to_string().contains("disappeared during commit") => {
// map mutated between commit's validation and removal loops: invariant breach, log it
}
Err(err) => return Err(err),
} Prevention
- Serialize manager access so commit completes without interleaving calls
- Audit any code that reacts to commit progress by mutating the manager
- Re-derive the pending set and retry commit for surviving services
- Treat recurrence as a concurrency bug, not a flake to ignore
When it happens
Trigger: A re-entrant manager call (kill, kill_all, cleanup, spawn) removing a PersistPending entry between the validation loop and the removal loop of the same commit invocation.
Common situations: Multi-threaded test harnesses sharing one ShellManager; new product code that reacts to commit progress by mutating the manager.
Related errors
- Persistent service {id} disappeared before commit
- Persistent service {id} has no releasable process id
- Persistent service {id} lost its process id
- Persistent service {id} exited before ownership transfer (st
- permissions changed after they were listed; reload {} and re
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/943c8e7c945ea5af.
Report an issue: GitHub.