Hmbown/CodeWhale · error · anyhow::Error
for
Error message
{COORDINATION_SAME_PROCESS_HANDOVER} for {}: {error} What it means
Delegated coordination for a state root is guarded by a cross-process lock file. When acquiring it fails, the code reads the lock file to learn the holder's PID; if that PID is this very process, the conflict is internal (a same-process handover deadlock/misordering) and this distinct error is thrown instead of blaming another Codewhale instance.
Solutions
- Check for re-entrant acquisition of the same coordination lock within this process and serialize those paths
- Delete the stale lock file under the state root if no coordination is actually active, then retry
- Investigate why the inner acquisition failed — the wrapped {error} names it (e.g. lock timeout or flock failure)
- Restart the process to clear in-memory lock state
Example fix
// before: same process re-acquires the lock it already holds
acquire_coordination_lock(&lock_path).await?;
// after: skip if this process already holds it
if !is_held_by_current_process(&lock_path) {
acquire_coordination_lock(&lock_path).await?;
} Defensive patterns
Strategy: retry
Validate before calling
let holder = std::fs::read_to_string(&lock_path).ok()
.and_then(|c| c.trim().parse::<u32>().ok());
if holder == Some(std::process::id()) {
// already held by this process: skip acquisition or reuse the existing guard
} Type guard
fn lock_held_by_current_process(lock_path: &Path) -> bool {
std::fs::read_to_string(lock_path).ok()
.and_then(|c| c.trim().parse::<u32>().ok())
== Some(std::process::id())
} Try / catch
match try_acquire_coordination(&lock_path) {
Ok(guard) => guard,
Err(e) if lock_held_by_current_process(&lock_path) => reuse_in_process_guard(),
Err(e) => return Err(anyhow!("coordination lock unavailable: {e}")),
} Prevention
- Serialize all coordination acquisitions within one process behind a single in-process mutex
- Clean up stale lock files on startup when the recorded PID is dead
- Never leave the coordination lock held across re-entrant call paths
- Use lock files with O_EXCL creation and PID content for diagnosis
When it happens
Trigger: Taking delegated coordination for state_root fails, and the lock file's stored PID parses to the current process ID — i.e. this process already holds (or stale-held) the coordination lock.
Common situations: Nested or re-entrant coordination acquisition in one process; a stale lock file left by a previous crashed instance that coincidentally reuses the current PID; spawning parallel coordination threads in the same process without releasing the lock.
Related errors
- another Codewhale process
- for
- This runtime is already active in another process. Close…
- Another pet recorder is using this output.
- Automation lock must be a regular file
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/f72cf2c1e9f57129.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:3261
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 433685b202)