langchain-ai/deepagents · error · TypeError

Expected PermissionRequestDecision, got {type(decision).__na

Error message

Expected PermissionRequestDecision, got {type(decision).__name__}

What it means

ClientHookService.permission_request validates that the hook handler returned a PermissionRequestDecision before the permission flow resolves a tool call. If the handler returns any other object (dict, None, wrong class), the service raises TypeError. This keeps the permission pipeline typed: downstream code expects fields like allow/deny on the decision.

Source

Thrown at libs/code/deepagents_code/hooks/client_lifecycle.py:285

        Returns:
            Aggregated permission decision.

        Raises:
            TypeError: If the runtime returns a mismatched decision type.
        """
        if not self.has_handlers(HookEvent.PERMISSION_REQUEST):
            return PermissionRequestDecision(
                event=HookEvent.PERMISSION_REQUEST,
                permission=PermissionEffect(behavior="none"),
            )
        decision = await self._invoke(
            context,
            PermissionRequestEvent(event=HookEvent.PERMISSION_REQUEST, call=call),
        )
        if not isinstance(decision, PermissionRequestDecision):
            msg = f"Expected PermissionRequestDecision, got {type(decision).__name__}"
            raise TypeError(msg)
        return decision

    async def resolve_permission(
        self,
        context: ClientHookContext,
        call: ToolCallData,
    ) -> PermissionHookOutcome:
        """Resolve a permission hook and present user-facing attribution once.

        The returned HITL decision carries the raw hook reason (or stop reason)
        for model-visible resume payloads. Attribution text is emitted only
        through the shared presenter.

        Args:
            context: Current client session context.
            call: Tool action awaiting approval.

        Returns:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Return PermissionRequestDecision.allow()/deny() (or model_validate the raw dict) from the PERMISSION_REQUEST hook
  2. Re-validate any deserialized decisions with PermissionRequestDecision before returning
  3. Ensure the callback is registered for permission_request and not reused from another hook event
  4. If a custom wrapper strips types, change it to pass through the original decision object

Example fix

// before
def check_perm(context, event):
    return {"allow": True}

// after
from deepagents_code.hooks import PermissionRequestDecision

def check_perm(context, event):
    return PermissionRequestDecision.allow()
Defensive patterns

Strategy: type-guard

Validate before calling

from deepagents_code.hooks import PermissionRequestDecision

result = my_permission_hook(context, event)
if not isinstance(result, PermissionRequestDecision):
    result = PermissionRequestDecision.model_validate(result)

Type guard

def is_permission_decision(value: object) -> bool:
    return isinstance(value, PermissionRequestDecision)

Try / catch

try:
    decision = await service.permission_request(context, call)
except TypeError as exc:
    logger.warning("permission hook returned invalid decision: %s", exc)
    decision = PermissionRequestDecision.deny(reason="invalid hook response")

Prevention

When it happens

Trigger: Calling permission_request() (or resolve_permission) when a registered PERMISSION_REQUEST hook returns {'allow': True}, None, or a decision class from another event instead of PermissionRequestDecision.

Common situations: Generic permission callbacks shared across tools returning ad-hoc dicts; older hook code written before the typed decision API; building the decision from persisted JSON without re-validating to the concrete class.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/ef3cb8395d8d21cb. Report an issue: GitHub.