Hmbown/CodeWhale · error · anyhow::Error

Persistent service {id} lost its process id

Error message

Persistent service {id} lost its process id

What it means

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.

Source

Thrown at crates/tui/src/tools/shell.rs:2912

                .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)
    }

    /// Kill only services waiting for a successful exec ownership transfer.
    /// Ordinary background jobs retain their existing manager lifetime.
    pub fn abort_persistent_services(&mut self) {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Treat as an internal bug: capture the task_id and reproduction steps
  2. Ensure nothing reaps or mutates manager children while commit runs
  3. After the failure, check for a lingering registered persistent process group for that service
Defensive patterns

Strategy: try-catch

Try / catch

match manager.commit_persistent_services() {
    Ok(receipts) => receipts,
    Err(err) if err.to_string().contains("lost its process id") => {
        // pid vanished between commit loops: check for a lingering registered
        // persistent process group, then report as an internal bug
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: 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).

Common situations: Same shape as the other mid-commit invariants: concurrent manager mutation or reaping; rare in single-threaded production use.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/825d7b1f78f6f318. Report an issue: GitHub.