{"record":{"id":"a24310b740e6381d","repo":"microsoft/autogen","slug":"closure-must-have-4-arguments","errorCode":null,"errorMessage":"Closure must have 4 arguments","messagePattern":"Closure must have 4 arguments","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-core/src/autogen_core/_closure_agent.py","lineNumber":31,"sourceCode":"from ._cancellation_token import CancellationToken\nfrom ._message_context import MessageContext\nfrom ._serialization import try_get_known_serializers_for_type\nfrom ._subscription import Subscription\nfrom ._subscription_context import SubscriptionInstantiationContext\nfrom ._topic import TopicId\nfrom ._type_helpers import get_types\nfrom .exceptions import CantHandleException\n\nT = TypeVar(\"T\")\nClosureAgentType = TypeVar(\"ClosureAgentType\", bound=\"ClosureAgent\")\n\n\ndef get_handled_types_from_closure(\n    closure: Callable[[ClosureAgent, T, MessageContext], Awaitable[Any]],\n) -> Sequence[type]:\n    args = inspect.getfullargspec(closure)[0]\n    if len(args) != 3:\n        raise AssertionError(\"Closure must have 4 arguments\")\n\n    message_arg_name = args[1]\n\n    type_hints = get_type_hints(closure)\n\n    if \"return\" not in type_hints:\n        raise AssertionError(\"return not found in function signature\")\n\n    # Get the type of the message parameter\n    target_types = get_types(type_hints[message_arg_name])\n    if target_types is None:\n        raise AssertionError(\"Message type not found\")\n\n    # print(type_hints)\n    return_types = get_types(type_hints[\"return\"])\n\n    if return_types is None:\n        raise AssertionError(\"Return type not found\")","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-core/src/autogen_core/_closure_agent.py#L13-L49","documentation":"get_handled_types_from_closure() inspects the closure passed to ClosureAgent and requires exactly 3 declared parameters (self/agent, message, ctx — the message says '4 arguments' counting the implicit bound instance). AssertionError fires when inspect.getfullargspec(closure)[0] has a different length.","triggerScenarios":"Passing a closure like async def handler(agent, ctx) (2 params) or async def handler(agent, msg, ctx, extra) (4 params) to ClosureAgent; passing a bound method or partial whose effective signature has a different arity.","commonSituations":"Writing closure agents quickly and miscounting parameters; refactoring a handler signature and forgetting the ClosureAgent call site; wrapping handlers with functools.partial.","solutions":["Use the exact signature async def closure(agent: ClosureContext, message: MsgType, ctx: MessageContext) -> ... and pass it unchanged.","If you wrapped the handler (partial, decorator), ensure the wrapper itself declares (agent, message, ctx).","Count parameters excluding *args/**kwargs — the check uses getfullargspec[0], so keyword-only args also count if listed."],"exampleFix":"# before\nasync def handler(agent, ctx):  # missing message param\n    ...\nClosureAgent(\"d\", handler)\n\n# after\nasync def handler(agent: ClosureContext, message: str, ctx: MessageContext) -> None:\n    ...\nClosureAgent(\"d\", handler)","handlingStrategy":"validation","validationCode":"import inspect\n\ndef closure_signature_ok(closure) -> bool:\n    spec = inspect.getfullargspec(closure)\n    return len(spec.args) == 3  # agent, message, ctx\n\nassert closure_signature_ok(my_closure), \"closure must be (agent, message, ctx)\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always write closures as async def(agent: ClosureContext, message: MsgT, ctx: MessageContext).","Check inspect.getfullargspec(closure).args == ['agent','message','ctx'] in a unit test for each closure.","Avoid partial()/decorators that change the declared arity."],"tags":["autogen-core","closure-agent","signature","inspection"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}