microsoft/autogen · error · ValueError

Return type {type(return_value)} not in return types {return

Error message

Return type {type(return_value)} not in return types {return_types}

What it means

After the handler coroutine returns, the wrapper verifies the returned value's concrete type() against the declared return types (unless Any is among them). With strict=True a mismatch raises ValueError; with strict=False it logs a warning and still returns the value. This guards RPC contracts where the declared produces type determines response serialization.

Source

Thrown at python/packages/autogen-core/src/autogen_core/_routed_agent.py:153

        if return_types is None:
            raise AssertionError("Return type not found")

        # Convert target_types to list and stash

        @wraps(func)
        async def wrapper(self: AgentT, message: ReceivesT, ctx: MessageContext) -> ProducesT:
            if type(message) not in target_types:
                if strict:
                    raise CantHandleException(f"Message type {type(message)} not in target types {target_types}")
                else:
                    logger.warning(f"Message type {type(message)} not in target types {target_types}")

            return_value = await func(self, message, ctx)

            if AnyType not in return_types and type(return_value) not in return_types:
                if strict:
                    raise ValueError(f"Return type {type(return_value)} not in return types {return_types}")
                else:
                    logger.warning(f"Return type {type(return_value)} not in return types {return_types}")

            return return_value

        wrapper_handler = cast(MessageHandler[AgentT, ReceivesT, ProducesT], wrapper)
        wrapper_handler.target_types = list(target_types)
        wrapper_handler.produces_types = list(return_types)
        wrapper_handler.is_message_handler = True
        wrapper_handler.router = match or (lambda _message, _ctx: True)

        return wrapper_handler

    if func is None and not callable(func):
        return decorator
    elif callable(func):
        return decorator(func)
    else:

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Return an instance of the exact annotated return type on every code path, including early returns
  2. Update the annotation to match what is actually returned (e.g. '-> Response | None' is not allowed for rpc; instead always return Response)
  3. If intentional, annotate the return as Any (the check is skipped when Any is in return_types) or pass strict=False to downgrade to a warning

Example fix

# before
@rpc
async def handle(self, message: Ask, ctx: MessageContext) -> Answer:
    if not ask.valid:
        return  # None -> ValueError

# after
@rpc
async def handle(self, message: Ask, ctx: MessageContext) -> Answer:
    if not ask.valid:
        return Answer(error="invalid")
Defensive patterns

Strategy: try-catch

Validate before calling

def returns_match(handler_wrapper, value) -> bool:
    from typing import Any
    return Any in handler_wrapper.produces_types or type(value) in handler_wrapper.produces_types

Type guard

def return_value_ok(produces_types, value) -> bool:
    return type(value) in produces_types

Try / catch

try:
    result = await agent.on_message(msg, ctx)
except ValueError as e:
    if "Return type" in str(e):
        logger.error("handler %s returned wrong type: %s", handler, e)
    raise

Prevention

When it happens

Trigger: Handler annotated '-> Response' but returns None (no explicit return), returns a subclass/dict/dataclass instead of Response, or returns an object whose exact type() differs from every type in the declared return union.

Common situations: Forgetting a return statement on an early-exit path; refactoring the response model while keeping the old annotation; returning a pydantic model copy constructed via a different class (e.g. model_construct of a sibling); unit tests calling handlers directly and asserting on the ValueError.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/1e9fba659f57e24c. Report an issue: GitHub.