Hmbown/CodeWhale · error
another Codewhale process
Error message
another Codewhale process{} owns delegated coordination for {}: {error} What it means
Delegated sub-agent coordination state is guarded by a cross-process lock file under state_root. When lock acquisition fails and the holder PID recorded in the lock file is a different process, this error reports which process (by PID) owns the coordination, so the caller knows the operation cannot proceed until that process exits or releases.
Solutions
- Identify and stop the other Codewhale process listed in the message (kill <pid>) or wait for it to finish.
- Use a different state_root / workspace so the two processes do not contend for the same delegated coordination.
- If the PID no longer exists (crashed holder), verify with ps -p <pid> and remove the stale lock file.
- Retry the operation once the other process has released coordination.
Example fix
// before: blind retry against a held lock
let result = spawn_delegated(state_root);
// after: check holder before attempting
let holder = std::fs::read_to_string(state_root.join("coordination.lock")).ok();
if holder_is_live_other_process(&holder) { return Err(/* surface holder pid, don't retry */); }
let result = spawn_delegated(state_root); Defensive patterns
Strategy: retry
Validate before calling
fn other_process_holds(state_root: &Path) -> Option<u32> {
std::fs::read_to_string(state_root.join("coordination.lock")).ok()
.and_then(|c| c.trim().parse::<u32>().ok())
.filter(|pid| *pid != std::process::id())
} Type guard
fn holder_is_external(state_root: &Path) -> bool {
other_process_holds(state_root).is_some()
} Try / catch
match acquire_delegated_coordination(state_root) {
Err(e) if e.to_string().contains("another Codewhale process") => {
eprintln!("coordination held elsewhere; retry after it exits");
backoff_retry(state_root, 3);
}
r => r?,
} Prevention
- Give each concurrent Codewhale instance its own state_root/workspace.
- Check the lock holder PID before long-running delegated operations.
- Clean up lock files after abnormal exits once you confirm the PID is dead.
When it happens
Trigger: Starting a delegated sub-agent operation whose state_root coordination lock file records a holder PID different from std::process::id(), while the lock acquisition attempt returned Err(error) (timeout or refusal).
Common situations: Two Codewhale instances (or a TUI and a CLI) sharing a workspace state_root; a crashed predecessor left a valid lock file; parallel agent runs in separate terminals on the same project.
Related errors
- Another pet recorder is using this output.
- for
- 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/eefef4e418423f53.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:3461
}
});
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}",
state_root.display()
))
}
}
}View on GitHub (pinned to 73e0f67d83)