Hmbown/CodeWhale · error
for
Error message
{COORDINATION_SAME_PROCESS_HANDOVER} for {}: {error} What it means
When a delegated sub-agent coordination lock handover fails, the code reads the lock file's holder PID. If that PID is the current process, it means the same Codewhale process is both releasing and acquiring the delegated coordination lock, which is a self-handover bug rather than external contention. The library throws this to surface the internal invariant violation instead of deadlocking or silently double-holding the lock.
Solutions
- Audit the code path that acquired the lock earlier in this process and ensure it releases it before re-acquiring (check for early returns / panics skipping release_tx drop).
- Check whether the same operation is being invoked re-entrantly in one process; serialize it instead.
- If the lock file is stale from a crashed run of the same PID, remove the stale lock file after confirming no live holder.
- File a bug with the inner {error} detail: same-process handover should not fail under normal operation.
Example fix
// before: acquire delegated lock again without releasing prior hold spawn_delegated_coordination(state_root, ...)?; // after: ensure the prior guard is dropped first drop(previous_coordination_guard); spawn_delegated_coordination(state_root, ...)?;
Defensive patterns
Strategy: try-catch
Validate before calling
let holder = std::fs::read_to_string(state_root.join("coordination.lock")).ok().and_then(|c| c.trim().parse::<u32>().ok());
if holder == Some(std::process::id()) { return Err("this process already holds delegated coordination"); } Type guard
fn held_by_self(state_root: &Path) -> bool {
std::fs::read_to_string(state_root.join("coordination.lock")).ok()
.and_then(|c| c.trim().parse::<u32>().ok())
== Some(std::process::id())
} Try / catch
match acquire_delegated_coordination(state_root) {
Err(e) if e.to_string().contains("same process") => release_local_holds_then_retry(state_root),
Err(e) => return Err(e),
Ok(guard) => guard,
} Prevention
- Ensure every acquired coordination guard is dropped on all exit paths (use RAII, avoid early returns before drop).
- Never call the delegated acquisition path re-entrantly in one process.
- Log lock acquire/release pairs to spot unbalanced handovers early.
When it happens
Trigger: Calling into the delegated coordination acquisition path (subagent tool spawn with delegation) while the lock file at state_root is already recorded as held by this same process ID, and the underlying lock acquisition returned Err(error).
Common situations: A previous acquisition in the same process leaked without releasing (e.g. a panic between create and drop), re-entrant calls into the delegation path, or stale in-memory bookkeeping after a retry within one process.
Related errors
- another Codewhale process
- Another pet recorder is using this output.
- for
- Runtime turn operation is already being claimed; retry
- Task store is busy; state is unavailable
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/f72cf2c1e9f57129.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:3456
let _ = release_rx.recv();
}
Err(error) => {
let _ = ready_tx.send(Err(error.to_string()));
}
}
});
match ready_rx.recv_timeout(Duration::from_secs(5)) {
Ok(Ok(())) => Ok(Self {
release: Some(release_tx),
thread: Some(thread),
}),
Ok(Err(error)) => {
let _ = thread.join();
let holder_pid = std::fs::read_to_string(&lock_path)
.ok()
.and_then(|contents| contents.trim().parse::<u32>().ok());
if holder_pid == Some(std::process::id()) {
Err(anyhow!(
"{COORDINATION_SAME_PROCESS_HANDOVER} for {}: {error}",
state_root.display()
))
} else {
Err(anyhow!(
"another Codewhale process{} owns delegated coordination for {}: {error}",
holder_pid
.map(|pid| format!(" (pid {pid})"))
.unwrap_or_default(),
state_root.display()
))
}
}
Err(error) => {
drop(release_tx);
let _ = thread.join();
Err(anyhow!(
"{COORDINATION_LOCK_TIMEOUT_MARKER} for {}: {error}",View on GitHub (pinned to 73e0f67d83)