Hmbown/CodeWhale · error · anyhow::Error
Tool Web is limited to search/fetch in the read-only…
Error message
Tool Web is limited to search/fetch in the read-only evidence profile
What it means
In the read-only evidence profile, Scout and Reviewer roles may use the Web tool only with `action` = `search` or `fetch`. Any other Web action is rejected, because catalog shaping alone is not authority — the per-action gate is re-checked at dispatch to stop hand-written calls from reaching restricted actions.
Solutions
- Restrict the Web call to `action: "search"` or `action: "fetch"`.
- If a non-read action is genuinely required, delegate to a role with write authority (implement/general).
- Check the action string for typos or unsupported verbs.
- Update the orchestrating instructions to state read-only web constraints up front.
Example fix
// before
Web.call({ action: "post", url: "https://api.example.com" })
// after
Web.call({ action: "fetch", url: "https://api.example.com" }) Defensive patterns
Strategy: validation
Validate before calling
if ((role === 'scout' || role === 'reviewer') && toolName === 'Web' &&
!['search','fetch'].includes(input.action)) {
throw new Error('web action not allowed in read-only profile');
} Type guard
const isReadOnlyWebAction = (a) => a === 'search' || a === 'fetch';
Try / catch
try { web.call(input); } catch (e) { if (String(e).includes('search/fetch')) { input.action = 'fetch'; retry(); } } Prevention
- Restrict generated web calls to search/fetch when delegating to read-only roles
- Route any web write/submission to a role with write authority
- Validate the action string against the allowed set before dispatch
When it happens
Trigger: A Scout or Reviewer sub-agent calls the Web tool with an action other than search/fetch (e.g. `post`, `download`, `submit`) — whether from the model improvising or from a hand-constructed tool call.
Common situations: A child attempts to submit a form or authenticate to a site while doing research; a parent with full web access had its task delegated to a read-only child that tries the same Web verbs; action name typo causes a match failure.
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
- Tool is not available to this read-only worker because its…
- agent action=claim widens an enforced write scope, and the…
- read-only roles may revise their private working notes
- Refusing from agent ' ' to ' '; a child may control only…
- Refusing on self (agent_id ' '); child coordination…
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/fe543803de2afa1e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/tools/subagent/mod.rs:17492
async fn execute_full(
&self,
agent_id: &str,
tool_id: &str,
name: &str,
input: Value,
) -> Result<RichToolResult> {
if self.role_blocks_unhardened_process_tool(name) {
return Err(anyhow!(
"Tool {name} is not available to this read-only worker because its process path does not share the hardened evidence boundary. Use read/search, classifier-bounded bash reads, or the verifier's bounded Run tool instead."
));
}
let action = input.get("action").and_then(Value::as_str);
if matches!(&self.agent_type, FleetRole::Scout | FleetRole::Reviewer)
&& name == "Web"
&& !matches!(action, Some("search" | "fetch"))
{
return Err(anyhow!(
"Tool Web is limited to search/fetch in the read-only evidence profile"
));
}
// Catalog shaping is not authority. `agent` clears both name-keyed
// gates below by design, so the per-action gate has to be repeated
// here or a hand-written call would reach an action the role's own
// catalog withheld.
if name == "agent"
&& matches!(parse_agent_tool_action(&input), Ok(AgentToolAction::Claim))
&& !self.agent_action_permitted("claim")
{
return Err(anyhow!(
"agent action=claim widens an enforced write scope, and the Fleet role `{role}` has no write authority to widen. Use an `implement` or `general` role.",
role = self.agent_type.as_str()
));
}
let family_action_allowed = if !Self::ACTION_ALIASES
.iter()View on GitHub (pinned to 433685b202)