microsoft/autogen · error · ValueError

Dataclass has a union type, which is not supported. To use a

Error message

Dataclass has a union type, which is not supported. To use a union, use a Pydantic model

What it means

DataclassJsonMessageSerializer refuses dataclasses whose fields contain union types (e.g. str | None, A | B). The dataclass-based JSON serializer does type-appending/dispatch that cannot represent a union unambiguously, so the constructor raises ValueError and directs you to Pydantic, whose serializer supports unions and nested models.

Source

Thrown at python/packages/autogen-core/src/autogen_core/_serialization.py:105

            if has_nested_base_model_in_type(arg):
                return True
    return False


DataclassT = TypeVar("DataclassT", bound=IsDataclass)

JSON_DATA_CONTENT_TYPE = "application/json"
"""JSON data content type"""

# TODO: what's the correct content type? There seems to be some disagreement over what it should be
PROTOBUF_DATA_CONTENT_TYPE = "application/x-protobuf"
"""Protobuf data content type"""


class DataclassJsonMessageSerializer(MessageSerializer[DataclassT]):
    def __init__(self, cls: type[DataclassT]) -> None:
        if contains_a_union(cls):
            raise ValueError("Dataclass has a union type, which is not supported. To use a union, use a Pydantic model")

        if has_nested_dataclass(cls) or has_nested_base_model(cls):
            raise ValueError(
                "Dataclass has nested dataclasses or base models, which are not supported. To use nested types, use a Pydantic model"
            )

        self.cls = cls

    @property
    def data_content_type(self) -> str:
        return JSON_DATA_CONTENT_TYPE

    @property
    def type_name(self) -> str:
        return _type_name(self.cls)

    def deserialize(self, payload: bytes) -> DataclassT:
        message_str = payload.decode("utf-8")

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Convert the message dataclass to a pydantic.BaseModel and use PYDANTICJsonMessageSerializer (or MessageRegistry with pydantic messages)
  2. Replace the union with a single concrete type (split into separate message classes per variant, or drop the None by using a default value)
  3. Register a custom MessageSerializer implementation for that specific dataclass

Example fix

# before
@dataclass
class Task:
    payload: str | None = None
runtime.add_message_serializer(DataclassJsonMessageSerializer(Task))

# after
class Task(BaseModel):
    payload: str | None = None
runtime.add_message_serializer(PYDANTICJsonMessageSerializer(Task))
Defensive patterns

Strategy: validation

Validate before calling

import dataclasses, typing

def dataclass_is_serializable(cls) -> bool:
    if not dataclasses.is_dataclass(cls):
        return True
    for f in dataclasses.fields(cls):
        if typing.get_args(f.type) and isinstance(f.type, typing.get_args(typing.Union[int, int])[0].__class__ if False else None):
            pass
    # robust check: reject unions
    def has_union(t):
        import typing
        return (typing.get_origin(t) is typing.Union)
    return not any(has_union(f.type) for f in dataclasses.fields(cls))

assert dataclass_is_serializable(MyDataclass), "use pydantic for union fields"

Type guard

from pydantic import BaseModel

def is_pydantic_message(msg_cls) -> bool:
    return isinstance(msg_cls, type) and issubclass(msg_cls, BaseModel)

Try / catch

try:
    runtime.add_message_serializer(DataclassJsonMessageSerializer(Msg))
except ValueError as e:
    if "union type" in str(e):
        runtime.add_message_serializer(PYDANTICJsonMessageSerializer(pydantic_version_of(Msg)))
    else:
        raise

Prevention

When it happens

Trigger: Constructing DataclassJsonMessageSerializer(MyDataclass) or calling runtime.add_message_serializer with it, where any field of MyDataclass (or the message type you register for publish/send) is annotated with a union type. Also triggered by message_registry APIs that wrap dataclasses in this serializer automatically.

Common situations: Migrating Protobuf/dataclass examples to richer messages with optional fields (str | None); mixing dataclass messages with pydantic-style typing; upgrading code from Optional[x] sugar to PEP 604 unions (x | None), which hits the same check.

Related errors


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