langchain-ai/deepagents · error · TypeError

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

Error message

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

What it means

Internal helper _require_decision narrows the generic HookDecision returned by hook execution to the concrete decision type each lifecycle event expects (e.g. SubagentStartDecision, PostToolUseDecision). Hook code that returns a decision object of the wrong concrete class is rejected with TypeError naming the expected and actual types.

Source

Thrown at libs/code/deepagents_code/hooks/server_middleware.py:771

                return {_STOP_STATE_KEY: 0}
            return None
        feedback = "\n".join(decision.feedback).strip() or (
            decision.stop_reason or "Continue working."
        )
        return {
            "messages": [HumanMessage(content=feedback)],
            "jump_to": "model",
            _STOP_STATE_KEY: continuation + 1,
        }


def _require_decision[DecisionT: BaseHookDecision](
    decision: HookDecision,
    expected: type[DecisionT],
) -> DecisionT:
    if not isinstance(decision, expected):
        msg = f"Expected {expected.__name__}, got {type(decision).__name__}"
        raise TypeError(msg)
    return decision


def _session_gate(runtime_context: object) -> _SessionHookGate | None:
    fields = _context_mapping(runtime_context)
    snapshot_id = fields.get("hooks_snapshot_id")
    events = fields.get("hooks_server_events")
    if not isinstance(snapshot_id, str) or not snapshot_id:
        return None
    if not isinstance(events, list) or not events:
        return None
    return {
        "snapshot_id": snapshot_id,
        "events": frozenset(str(item) for item in events),
    }


def _event_enabled(gate: _SessionHookGate | None, event: HookEvent) -> bool:

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Return the decision class matching the event: check the hook event's documented decision type and construct that (e.g. PostToolUseDecision for post-tool-use hooks).
  2. If a handler serves multiple events, branch inside it on the event and build the correct decision type per event.
  3. Catch TypeError from agent execution and inspect the message ("Expected X, got Y") to identify which hook returned the wrong type.
  4. Add type hints on the handler (-> CorrectDecision) so static checkers flag mismatches before runtime.

Example fix

// before
@hooks.on(HookEvent.POST_TOOL_USE)
def guard(event) -> ModelRequestDecision:  # wrong decision type
    return ModelRequestDecision(approve=True)

// after
@hooks.on(HookEvent.POST_TOOL_USE)
def guard(event) -> PostToolUseDecision:
    return PostToolUseDecision(approve=True)
Defensive patterns

Strategy: type-guard

Validate before calling

decision = handler(event)
if not isinstance(decision, ExpectedDecision):
    raise TypeError(f"{handler} must return {ExpectedDecision.__name__}")

Type guard

def is_decision[D: BaseHookDecision](value: object, expected: type[D]) -> TypeGuard[D]:
    return isinstance(value, expected)

Try / catch

try:
    agent.invoke(state, config)
except TypeError as exc:
    if str(exc).startswith("Expected ") and "got " in str(exc):
        log.error("hook returned wrong decision type", error=str(exc))

Prevention

When it happens

Trigger: A hook handler registered for one event type returns a decision class belonging to a different event (e.g. returning a ModelRequestDecision from a SubagentStart hook), or a handler returns a BaseHookDecision subclass not matching `expected` in any of the six calling hooks (_maybe_subagent_start, _after_model, _maybe_post_tool_use, _maybe_subagent_stop, _after_agent, _pre_auto_compact).

Common situations: Reusing one handler function across multiple hook events with a single shared decision type; copying an example hook whose decision class doesn't match the registered event; typos in imports pulling the wrong Decision class.

Related errors


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