{"record":{"id":"fa740d92ddd84a31","repo":"langchain-ai/deepagents","slug":"expected-expected-name-got-type-decision","errorCode":null,"errorMessage":"Expected {expected.__name__}, got {type(decision).__name__}","messagePattern":"Expected (.+?), got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/hooks/server_middleware.py","lineNumber":771,"sourceCode":"                return {_STOP_STATE_KEY: 0}\n            return None\n        feedback = \"\\n\".join(decision.feedback).strip() or (\n            decision.stop_reason or \"Continue working.\"\n        )\n        return {\n            \"messages\": [HumanMessage(content=feedback)],\n            \"jump_to\": \"model\",\n            _STOP_STATE_KEY: continuation + 1,\n        }\n\n\ndef _require_decision[DecisionT: BaseHookDecision](\n    decision: HookDecision,\n    expected: type[DecisionT],\n) -> DecisionT:\n    if not isinstance(decision, expected):\n        msg = f\"Expected {expected.__name__}, got {type(decision).__name__}\"\n        raise TypeError(msg)\n    return decision\n\n\ndef _session_gate(runtime_context: object) -> _SessionHookGate | None:\n    fields = _context_mapping(runtime_context)\n    snapshot_id = fields.get(\"hooks_snapshot_id\")\n    events = fields.get(\"hooks_server_events\")\n    if not isinstance(snapshot_id, str) or not snapshot_id:\n        return None\n    if not isinstance(events, list) or not events:\n        return None\n    return {\n        \"snapshot_id\": snapshot_id,\n        \"events\": frozenset(str(item) for item in events),\n    }\n\n\ndef _event_enabled(gate: _SessionHookGate | None, event: HookEvent) -> bool:","sourceCodeStart":753,"sourceCodeEnd":789,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/hooks/server_middleware.py#L753-L789","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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).","If a handler serves multiple events, branch inside it on the event and build the correct decision type per event.","Catch TypeError from agent execution and inspect the message (\"Expected X, got Y\") to identify which hook returned the wrong type.","Add type hints on the handler (-> CorrectDecision) so static checkers flag mismatches before runtime."],"exampleFix":"// before\n@hooks.on(HookEvent.POST_TOOL_USE)\ndef guard(event) -> ModelRequestDecision:  # wrong decision type\n    return ModelRequestDecision(approve=True)\n\n// after\n@hooks.on(HookEvent.POST_TOOL_USE)\ndef guard(event) -> PostToolUseDecision:\n    return PostToolUseDecision(approve=True)","handlingStrategy":"type-guard","validationCode":"decision = handler(event)\nif not isinstance(decision, ExpectedDecision):\n    raise TypeError(f\"{handler} must return {ExpectedDecision.__name__}\")","typeGuard":"def is_decision[D: BaseHookDecision](value: object, expected: type[D]) -> TypeGuard[D]:\n    return isinstance(value, expected)","tryCatchPattern":"try:\n    agent.invoke(state, config)\nexcept TypeError as exc:\n    if str(exc).startswith(\"Expected \") and \"got \" in str(exc):\n        log.error(\"hook returned wrong decision type\", error=str(exc))","preventionTips":["Match each event's documented decision class exactly","Don't share one decision-returning handler across different hook events","Annotate handler return types and run a static type checker","Import decision classes carefully to avoid same-named wrong types"],"tags":["hooks","type-error","decision"],"backgroundTag":"hook-return-type-mismatch","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}