{"record":{"id":"452e1cf7e8538c14","repo":"Hmbown/CodeWhale","slug":"shell-readonly-command-tool-name-input-did-not-match-the","errorCode":null,"errorMessage":"[shell.readonly.command] Tool {name} input did not match the bounded read-only shell grammar for Fleet role `{role}`. {guidance}","messagePattern":"\\[shell\\.readonly\\.command\\] Tool (.+?) input did not match the bounded read-only shell grammar for Fleet role `(.+?)`\\. (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":17530,"sourceCode":"        {\n            true\n        } else if let Some(action) = action {\n            self.is_action_allowed(name, action)\n        } else {\n            self.allowed_tools\n                .as_ref()\n                .is_none_or(|list| list.iter().any(|allowed| allowed == name))\n        };\n        if !self.is_tool_allowed(name) || !family_action_allowed {\n            return Err(anyhow!(\"Tool {name} not allowed for this sub-agent\"));\n        }\n        // #3217: authoritative per-role posture — read-only roles cannot mutate\n        // and non-`Full`-shell roles cannot run shell, regardless of whether\n        // the parent session is auto-approved. This closes the auto-approve\n        // bypass where a read-only child could quietly write or shell out.\n        if !self.posture_permits_tool(name, Some(&input)) {\n            if self.allows_bounded_readonly_bash(name) {\n                return Err(anyhow!(\n                    \"[shell.readonly.command] Tool {name} input did not match the bounded read-only shell grammar for Fleet role `{role}`. {guidance}\",\n                    role = self.agent_type.as_str(),\n                    guidance = codewhale_execpolicy::command_safety::readonly_command_help()\n                ));\n            }\n            return Err(anyhow!(\n                \"[role.posture.denied] Tool {name} is not permitted for the read-only Fleet role `{role}`. Use an `implement` or `general` role (or `custom` with an explicit allowed_tools list) to mutate the workspace or run shell commands.\",\n                role = self.agent_type.as_str()\n            ));\n        }\n        // Denied network capability cannot be expanded by answering a prompt.\n        if self.network_is_denied() {\n            reject_network_reaching_input(name, &input)?;\n        }\n        // The session's permission posture, applied to this child exactly as\n        // it is applied to the parent turn: the deterministic Auto-Review\n        // floor first, then (Auto-Review) the model guardian for holds it\n        // could not prove safe, or (Ask) a prompt raised in the parent's UI.","sourceCodeStart":17512,"sourceCodeEnd":17548,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tools/subagent/mod.rs#L17512-L17548","documentation":"A sub-agent whose Fleet role is read-only tried to run a shell command that did not match the bounded read-only shell grammar. The library enforces per-role posture authoritatively (#3217): read-only roles cannot mutate or run arbitrary shell, even if the parent session is auto-approved. The command string failed `allows_bounded_readonly_bash` / the exec-policy grammar check, so the call is rejected with role-specific guidance.","triggerScenarios":"Calling a bash/shell tool from a sub-agent whose agent_type is a read-only Fleet role (e.g. a review/analysis role) with a command string that is not on the proven read-only allowlist (e.g. contains redirects, pipes to writers, or mutating binaries), or calling any non-bounded-shell tool that posture_permits_tool rejects while the tool happens to allow bounded readonly bash.","commonSituations":"Delegating an `implement`-style task to a read-only role; a child agent hallucinating `git push`, `rm`, or file-redirecting commands; relying on parent auto-approve to relax child restrictions (explicitly not permitted).","solutions":["Rewrite the command to a form accepted by codewhale_execpolicy::command_safety (pure read-only: no redirects, no mutation, no arbitrary program execution).","Re-dispatch the task with an `implement`, `general`, or `custom` role that has shell/write capability.","If the child only needs bounded reads, use explicit read tools (read/grep/glob) instead of bash.","Check readonly_command_help() output included in the error for the accepted grammar."],"exampleFix":"// before (read-only role)\n{\"tool\":\"bash\",\"input\":{\"command\":\"git status > /tmp/out.txt\"}}\n// after: bounded read-only form, or switch role to `implement`\n{\"tool\":\"bash\",\"input\":{\"command\":\"git status\"}}","handlingStrategy":"validation","validationCode":"// before dispatching bash to a child\nif role_is_readonly(role) && !codewhale_execpolicy::command_safety::is_readonly_command(cmd) {\n    return Err(\"command not in bounded read-only grammar for role\");\n}","typeGuard":"fn is_bounded_readonly_bash(name: &str, input: &Value) -> bool {\n    name == \"bash\"\n        && input.get(\"command\").and_then(Value::as_str)\n            .map(codewhale_execpolicy::command_safety::is_readonly_command)\n            .unwrap_or(false)\n}","tryCatchPattern":"match child.execute_tool(name, input) {\n    Err(e) if e.to_string().contains(\"[shell.readonly.command]\") => {\n        // fall back to read tools or re-dispatch with write-capable role\n    }\n    other => other?,\n}","preventionTips":["Keep read-only roles on explicit read/grep/glob tools instead of bash.","Pre-validate shell strings with codewhale_execpolicy::command_safety before dispatch.","Do not rely on parent auto-approve to relax child posture.","Consult readonly_command_help() for accepted grammar when composing commands."],"tags":["rust","subagent","permissions","shell"],"backgroundTag":"permission-denied","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}