{"record":{"id":"8d296841bef6b9da","repo":"NousResearch/hermes-agent","slug":"per-tool-blocking-is-not-supported-use-allowed-to","errorCode":null,"errorMessage":"Per-tool blocking is not supported; use allowed_toolsets. Hermes always blocks unsafe child tools.","messagePattern":"Per-tool blocking is not supported; use allowed_toolsets\\. Hermes always blocks unsafe child tools\\.","errorType":"exception","errorClass":"SubagentLifecycleError","httpStatus":null,"severity":"error","filePath":"agent/subagent_lifecycle.py","lineNumber":515,"sourceCode":"        if request.context is not None and (\n            not isinstance(request.context, str)\n            or len(request.context) > _MAX_CONTEXT_CHARS\n        ):\n            raise SubagentLifecycleError(\n                \"context must be a string of at most 32000 characters.\"\n            )\n        if request.role not in {\"leaf\", \"orchestrator\"}:\n            raise SubagentLifecycleError(\"role must be 'leaf' or 'orchestrator'.\")\n        if request.timeout_seconds is not None:\n            raise SubagentLifecycleError(\n                \"Per-launch timeout is not supported; configure delegation timeout explicitly.\"\n            )\n        if request.working_directory is not None:\n            raise SubagentLifecycleError(\n                \"working_directory is not supported because Hermes delegates use isolated task environments.\"\n            )\n        if request.blocked_tools:\n            raise SubagentLifecycleError(\n                \"Per-tool blocking is not supported; use allowed_toolsets. Hermes always blocks unsafe child tools.\"\n            )\n        try:\n            metadata_bytes = len(\n                json.dumps(dict(request.metadata), sort_keys=True).encode()\n            )\n        except (TypeError, ValueError) as exc:\n            raise SubagentLifecycleError(\"metadata must be JSON-serializable.\") from exc\n        if metadata_bytes > _MAX_METADATA_BYTES:\n            raise SubagentLifecycleError(\"metadata exceeds 8192 bytes.\")\n        if request.allowed_toolsets:\n            from toolsets import TOOLSETS\n\n            unknown = set(request.allowed_toolsets) - set(TOOLSETS)\n            if unknown:\n                raise SubagentLifecycleError(\n                    f\"Unknown toolsets: {', '.join(sorted(unknown))}.\"\n                )","sourceCodeStart":497,"sourceCodeEnd":533,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/subagent_lifecycle.py#L497-L533","documentation":"Validation error from SubagentLifecycleManager._validate_request(): per-tool blocking is not supported. If request.blocked_tools is truthy the launch is rejected; the security model is allow-list based — restrict a child via allowed_toolsets, and Hermes itself always strips unsafe tools from children.","triggerScenarios":"Setting SubagentLaunchRequest(blocked_tools=[\"terminal\"]) expecting a read-only child; porting a deny-list security policy from another framework; populating blocked_tools with an empty-but-truthy value like [\"\"] or [None].","commonSituations":"Trying to sandbox children by denying dangerous tools; policy engines that always emit a blocked list (even empty-looking); misunderstanding that a truthy-but-useless list still trips the check.","solutions":["Use allowed_toolsets with an explicit allow list (e.g. [\"search\", \"file\"]) instead of a deny list.","Clear blocked_tools entirely (empty list or None-equivalent) on every request.","Rely on Hermes' built-in child hardening for unsafe tools rather than adding your own deny list."],"exampleFix":"# before\nrequest = SubagentLaunchRequest(goal=g, blocked_tools=[\"terminal\", \"browser_navigate\"])\n\n# after\nrequest = SubagentLaunchRequest(goal=g, allowed_toolsets=[\"search\", \"file\", \"web\"])","handlingStrategy":"validation","validationCode":"request = SubagentLaunchRequest(\n    goal=goal,\n    allowed_toolsets=[\"search\", \"file\"],  # allow-list, not deny-list\n    blocked_tools=None,\n)","typeGuard":null,"tryCatchPattern":"try:\n    manager.launch(request)\nexcept SubagentLifecycleError as exc:\n    if \"Per-tool blocking\" in str(exc):\n        request = dataclasses.replace(request, blocked_tools=None)\n        manager.launch(request)\n    else:\n        raise","preventionTips":["Model child restrictions as an allow-list via allowed_toolsets.","Never populate blocked_tools, even with placeholder values.","Trust Hermes' built-in stripping of unsafe child tools."],"tags":["subagents","delegation","validation","security"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}