NousResearch/hermes-agent · error · SubagentLifecycleError

Per-tool blocking is not supported; use allowed_toolsets. He

Error message

Per-tool blocking is not supported; use allowed_toolsets. Hermes always blocks unsafe child tools.

What it means

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.

Source

Thrown at agent/subagent_lifecycle.py:515

        if request.context is not None and (
            not isinstance(request.context, str)
            or len(request.context) > _MAX_CONTEXT_CHARS
        ):
            raise SubagentLifecycleError(
                "context must be a string of at most 32000 characters."
            )
        if request.role not in {"leaf", "orchestrator"}:
            raise SubagentLifecycleError("role must be 'leaf' or 'orchestrator'.")
        if request.timeout_seconds is not None:
            raise SubagentLifecycleError(
                "Per-launch timeout is not supported; configure delegation timeout explicitly."
            )
        if request.working_directory is not None:
            raise SubagentLifecycleError(
                "working_directory is not supported because Hermes delegates use isolated task environments."
            )
        if request.blocked_tools:
            raise SubagentLifecycleError(
                "Per-tool blocking is not supported; use allowed_toolsets. Hermes always blocks unsafe child tools."
            )
        try:
            metadata_bytes = len(
                json.dumps(dict(request.metadata), sort_keys=True).encode()
            )
        except (TypeError, ValueError) as exc:
            raise SubagentLifecycleError("metadata must be JSON-serializable.") from exc
        if metadata_bytes > _MAX_METADATA_BYTES:
            raise SubagentLifecycleError("metadata exceeds 8192 bytes.")
        if request.allowed_toolsets:
            from toolsets import TOOLSETS

            unknown = set(request.allowed_toolsets) - set(TOOLSETS)
            if unknown:
                raise SubagentLifecycleError(
                    f"Unknown toolsets: {', '.join(sorted(unknown))}."
                )

View on GitHub (pinned to c896c09c42)

Solutions

  1. Use allowed_toolsets with an explicit allow list (e.g. ["search", "file"]) instead of a deny list.
  2. Clear blocked_tools entirely (empty list or None-equivalent) on every request.
  3. Rely on Hermes' built-in child hardening for unsafe tools rather than adding your own deny list.

Example fix

# before
request = SubagentLaunchRequest(goal=g, blocked_tools=["terminal", "browser_navigate"])

# after
request = SubagentLaunchRequest(goal=g, allowed_toolsets=["search", "file", "web"])
Defensive patterns

Strategy: validation

Validate before calling

request = SubagentLaunchRequest(
    goal=goal,
    allowed_toolsets=["search", "file"],  # allow-list, not deny-list
    blocked_tools=None,
)

Try / catch

try:
    manager.launch(request)
except SubagentLifecycleError as exc:
    if "Per-tool blocking" in str(exc):
        request = dataclasses.replace(request, blocked_tools=None)
        manager.launch(request)
    else:
        raise

Prevention

When it happens

Trigger: 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].

Common situations: 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.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/8d296841bef6b9da. Report an issue: GitHub.