{"record":{"id":"f47b47622e0c0a92","repo":"microsoft/autogen","slug":"dataclass-has-a-union-type-which-is-not-supported","errorCode":null,"errorMessage":"Dataclass has a union type, which is not supported. To use a union, use a Pydantic model","messagePattern":"Dataclass has a union type, which is not supported\\. To use a union, use a Pydantic model","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-core/src/autogen_core/_serialization.py","lineNumber":105,"sourceCode":"            if has_nested_base_model_in_type(arg):\n                return True\n    return False\n\n\nDataclassT = TypeVar(\"DataclassT\", bound=IsDataclass)\n\nJSON_DATA_CONTENT_TYPE = \"application/json\"\n\"\"\"JSON data content type\"\"\"\n\n# TODO: what's the correct content type? There seems to be some disagreement over what it should be\nPROTOBUF_DATA_CONTENT_TYPE = \"application/x-protobuf\"\n\"\"\"Protobuf data content type\"\"\"\n\n\nclass DataclassJsonMessageSerializer(MessageSerializer[DataclassT]):\n    def __init__(self, cls: type[DataclassT]) -> None:\n        if contains_a_union(cls):\n            raise ValueError(\"Dataclass has a union type, which is not supported. To use a union, use a Pydantic model\")\n\n        if has_nested_dataclass(cls) or has_nested_base_model(cls):\n            raise ValueError(\n                \"Dataclass has nested dataclasses or base models, which are not supported. To use nested types, use a Pydantic model\"\n            )\n\n        self.cls = cls\n\n    @property\n    def data_content_type(self) -> str:\n        return JSON_DATA_CONTENT_TYPE\n\n    @property\n    def type_name(self) -> str:\n        return _type_name(self.cls)\n\n    def deserialize(self, payload: bytes) -> DataclassT:\n        message_str = payload.decode(\"utf-8\")","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-core/src/autogen_core/_serialization.py#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Convert the message dataclass to a pydantic.BaseModel and use PYDANTICJsonMessageSerializer (or MessageRegistry with pydantic messages)","Replace the union with a single concrete type (split into separate message classes per variant, or drop the None by using a default value)","Register a custom MessageSerializer implementation for that specific dataclass"],"exampleFix":"# before\n@dataclass\nclass Task:\n    payload: str | None = None\nruntime.add_message_serializer(DataclassJsonMessageSerializer(Task))\n\n# after\nclass Task(BaseModel):\n    payload: str | None = None\nruntime.add_message_serializer(PYDANTICJsonMessageSerializer(Task))","handlingStrategy":"validation","validationCode":"import dataclasses, typing\n\ndef dataclass_is_serializable(cls) -> bool:\n    if not dataclasses.is_dataclass(cls):\n        return True\n    for f in dataclasses.fields(cls):\n        if typing.get_args(f.type) and isinstance(f.type, typing.get_args(typing.Union[int, int])[0].__class__ if False else None):\n            pass\n    # robust check: reject unions\n    def has_union(t):\n        import typing\n        return (typing.get_origin(t) is typing.Union)\n    return not any(has_union(f.type) for f in dataclasses.fields(cls))\n\nassert dataclass_is_serializable(MyDataclass), \"use pydantic for union fields\"","typeGuard":"from pydantic import BaseModel\n\ndef is_pydantic_message(msg_cls) -> bool:\n    return isinstance(msg_cls, type) and issubclass(msg_cls, BaseModel)","tryCatchPattern":"try:\n    runtime.add_message_serializer(DataclassJsonMessageSerializer(Msg))\nexcept ValueError as e:\n    if \"union type\" in str(e):\n        runtime.add_message_serializer(PYDANTICJsonMessageSerializer(pydantic_version_of(Msg)))\n    else:\n        raise","preventionTips":["Standardize all wire messages on pydantic BaseModel from the start","Add a unit test that constructs the serializer for every registered message type","Ban PEP 604 unions (x | None) in dataclass message fields via lint/type-check rules"],"tags":["autogen-core","serialization","dataclass","union","pydantic"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}