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

  1. 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).
  2. Check whether the same operation is being invoked re-entrantly in one process; serialize it instead.
  3. If the lock file is stale from a crashed run of the same PID, remove the stale lock file after confirming no live holder.
  4. 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

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


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)