{"record":{"id":"4e7939d32cea9cc1","repo":"Hmbown/CodeWhale","slug":"role-posture-denied-tool-name-is-not-permitted-for-the-read","errorCode":null,"errorMessage":"[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.","messagePattern":"\\[role\\.posture\\.denied\\] Tool (.+?) is not permitted for the read-only Fleet role `(.+?)`\\. Use an `implement` or `general` role \\(or `custom` with an explicit allowed_tools list\\) to mutate the workspace or run shell commands\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":17536,"sourceCode":"                .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.\n        // Full Access still fails closed on the non-bypassable safety floor.\n        // Role posture and the execution envelope below stay authoritative:\n        // this gate can only decide whether a call the role permits also\n        // clears the session's approval boundary.\n        if let ChildGateVerdict::Deny(reason) =\n            self.gate_held_call(agent_id, tool_id, name, &input).await","sourceCodeStart":17518,"sourceCodeEnd":17554,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tools/subagent/mod.rs#L17518-L17554","documentation":"The sub-agent's Fleet role is read-only and the tool it called is categorically not permitted by the role posture — unlike error 2610, the tool is not even a candidate for bounded read-only shell grammar. The posture check (#3217) hard-denies mutation and shell for read-only roles regardless of parent-session auto-approval.","triggerScenarios":"A child agent with a read-only Fleet role calls any mutating tool (write/edit/patch style tools) or a shell tool, and `allows_bounded_readonly_bash(name)` returns false, so the generic posture denial branch fires.","commonSituations":"Configuring a sub-agent with a review/analysis role but the model attempts file edits or command execution; mislabeling a `custom` agent without an `allowed_tools` list so it defaults to read-only posture.","solutions":["Use an `implement` or `general` role for the sub-agent, or define the role as `custom` with an explicit `allowed_tools` list including the tools it needs.","Keep mutation work out of read-only children: have them report findings and let a write-capable member apply changes.","Verify agent_type.as_str() for the dispatched child matches the intended role in your Fleet configuration."],"exampleFix":"// before\n{\"subagent\":{\"role\":\"review\",\"tools\":[\"edit\"]}}\n// after\n{\"subagent\":{\"role\":\"implement\"}}","handlingStrategy":"validation","validationCode":"if !role_allows_mutation(role) && write_family_tools.iter().any(|t| requested_tools.contains(t)) {\n    return Err(\"role {} cannot run tool {}\".format(role, tool));\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = child.execute_tool(name, input) {\n    if e.to_string().contains(\"[role.posture.denied]\") {\n        // re-dispatch with an `implement`/`general`/custom-allowed_tools role\n    }\n}","preventionTips":["Match role posture to the task's mutation needs at dispatch time.","Give `custom` roles an explicit allowed_tools list.","Have read-only children report findings; let write-capable members mutate.","Log role.as_str() alongside tool calls in tests to catch mismatches early."],"tags":["rust","subagent","permissions","role"],"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"}