{"record":{"id":"0aa07dbe01b1e235","repo":"Hmbown/CodeWhale","slug":"tool-name-not-allowed-for-this-sub-agent","errorCode":null,"errorMessage":"Tool {name} not allowed for this sub-agent","messagePattern":"Tool (.+?) not allowed for this sub-agent","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":17522,"sourceCode":"            return Err(anyhow!(\n                \"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.\",\n                role = self.agent_type.as_str()\n            ));\n        }\n        let family_action_allowed = if !Self::ACTION_ALIASES\n            .iter()\n            .any(|(family, _, _)| *family == name)\n        {\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        }","sourceCodeStart":17504,"sourceCodeEnd":17540,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tools/subagent/mod.rs#L17504-L17540","documentation":"The sub-agent's dispatch layer enforces its tool allow-list and per-family action permissions: if `is_tool_allowed(name)` is false (no allow-list membership) or the tool's family action is not permitted, the call is rejected with this generic not-allowed error. It precedes the deeper posture checks (read-only/no-shell enforcement).","triggerScenarios":"A sub-agent calls a tool whose name is not in its `allowed_tools` list (when one is set), or invokes a permitted tool with an action the family gate does not allow (`family_action_allowed` false).","commonSituations":"The parent's spawn config omitted a tool from the child's allow-list; the child tries a tool the parent restricted via the fingerprint's allow/deny lists; a family-action alias doesn't match the allowed set.","solutions":["Add the tool name to the sub-agent's `allowed_tools` list in the spawn/receipt configuration.","Check the fingerprint's allow/deny fields and regenerate the receipt including the needed tool.","Verify the family action is permitted for this role, or use an allowed action variant.","Check tool-name spelling against the catalog (name-keyed matching is exact)."],"exampleFix":"// before\nspawn_agent(\"v1;...;allow=read,search;...\"); // child then calls Write\n// after\nspawn_agent(\"v1;...;allow=read,search,write;...\");","handlingStrategy":"validation","validationCode":"const allowed = childConfig.allowedTools;\nif (allowed && !allowed.includes(toolName)) {\n  throw new Error(`${toolName} not in child allow-list`);\n}","typeGuard":"const isAllowedForChild = (name) => !childConfig.allowedTools || childConfig.allowedTools.includes(name);","tryCatchPattern":"try { result = await callTool(name, input); } catch (e) { if (String(e) === `Tool ${name} not allowed for this sub-agent`) { throw new Error(`add '${name}' to the spawn allow-list or use an allowed tool`); } }","preventionTips":["Keep the spawn allow-list in sync with the tools the delegated task requires","Regenerate fingerprints after changing allow/deny configuration","Match tool names exactly as they appear in the catalog"],"tags":["subagent","permissions","allowlist","tools"],"backgroundTag":"permission-denied","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}