Hmbown/CodeWhale · error · anyhow::Error

Tool cannot prove a bounded file target or read-only…

Error message

Tool {name} cannot prove a bounded file target or read-only execution while peers are writing in this shared checkout (blocking peers: {}). Use a bounded write tool, a proven read-only command, or bash with read_only=true for analysis under native enforcement. Executable work that needs writes requires worktree isolation. Disjoint write_roots alone do not constrain arbitrary code.

What it means

While another agent holds a shared write claim in the same checkout, a tool call from this sub-agent could not prove it is either a bounded file write or a read-only execution, and live blocking peers exist. The library refuses unverifiable operations under concurrent shared writes because disjoint write_roots cannot constrain arbitrary code execution; work that writes must use worktree isolation.

Solutions

  1. Use a bounded write tool (which takes a scoped claim) instead of arbitrary execution.
  2. Re-run the command as a provable read-only command, or set `read_only: true` on the bash input for pure analysis.
  3. Wait for blocking peers to release their shared write claims, then retry.
  4. Move write-needing executable work into an isolated worktree and re-dispatch there.

Example fix

// before
{"tool":"bash","input":{"command":"cargo test foo -- --nocapture"}}
// after (analysis under native enforcement)
{"tool":"bash","input":{"command":"cargo test foo -- --nocapture","read_only":true}}
Defensive patterns

Strategy: validation

Validate before calling

if coordination.live_peer_shared_write_claim_owners(me).len() > 0
    && !is_bounded_write(name)
    && !is_provably_readonly(name, &input)
{
    // defer or isolate before calling
}

Type guard

fn safe_under_shared_writes(name: &str, input: &Value) -> bool {
    is_bounded_write_tool(name)
        || input.get("read_only").and_then(Value::as_bool).unwrap_or(false)
        || codewhale_execpolicy::command_safety::is_readonly_command(
            input.get("command").and_then(Value::as_str).unwrap_or(""))
}

Try / catch

match result {
    Err(e) if e.to_string().contains("blocking peers:") => {
        // wait for peer claims to clear or retry with read_only=true
    }
    other => other?,
}

Prevention

When it happens

Trigger: A peer sub-agent holds a live shared write claim (`live_peer_shared_write_claim_owners` non-empty) and this agent invokes a tool (typically bash without read_only=true) that neither mutates a bounded path nor passes the read-only command proof.

Common situations: Multiple agents sharing one workspace checkout where one is editing while another runs arbitrary shell; running build/test commands with extra args during a peer's write session; write_roots configured expecting isolation that the enforcer does not trust for unbounded tools.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/3838b337d6038d5d. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/tools/subagent/mod.rs:17639

                                        ToolCapability::WritesFiles
                                            | ToolCapability::ExecutesCode
                                            | ToolCapability::Network
                                    )
                                })))
                }))
        {
            let manager = self.coordination_manager.read().await;
            // Only a *contended* shared checkout needs this gate. The claim
            // exists so concurrent children cannot overwrite each other; a lone
            // writer has no peer to collide with, and blocking it there bought
            // no safety while making a builder unable to run ordinary shell
            // work in the workspace the operator actually watches — worktree
            // isolation "fixes" that by writing somewhere they never see.
            if manager.shared_write_claim(&self.owner_agent_id).is_some() {
                let blocking_peers =
                    manager.live_peer_shared_write_claim_owners(&self.owner_agent_id);
                if !blocking_peers.is_empty() {
                    return Err(anyhow!(
                        "Tool {name} cannot prove a bounded file target or read-only execution while peers are writing in this shared checkout (blocking peers: {}). Use a bounded write tool, a proven read-only command, or bash with read_only=true for analysis under native enforcement. Executable work that needs writes requires worktree isolation. Disjoint write_roots alone do not constrain arbitrary code.",
                        blocking_peers.join(", ")
                    ));
                }
            }
        }
        let context = self
            .registry
            .context()
            .clone()
            .with_owner_agent(self.owner_agent_id.clone(), self.owner_agent_name.clone());
        let observed_paths = if scope_aware_write {
            mutation_paths(name, &input)?
        } else {
            Vec::new()
        };
        let outcome = self
            .registry

View on GitHub (pinned to 433685b202)