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

  1. Serialize all ShellManager access so commit runs to completion without interleaving calls
  2. Treat as a bug: log the task_id and audit what mutated the map mid-commit
  3. 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

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


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