{"id":"f1e2070615e3a2df","repo":"pydantic/pydantic","slug":"discriminator-alias-type","errorCode":"discriminator-alias-type","errorMessage":"Alias {alias!r} is not supported in a discriminated union","messagePattern":"Alias (.+?) is not supported in a discriminated union","errorType":"error_code","errorClass":"PydanticUserError","httpStatus":null,"severity":"error","filePath":"pydantic/_internal/_discriminated_union.py","lineNumber":419,"sourceCode":"        self, choice: core_schema.DataclassArgsSchema, source_name: str | None = None\n    ) -> list[str | int]:\n        source = 'DataclassArgs' if source_name is None else f'Dataclass {source_name!r}'\n        for field in choice['fields']:\n            if field['name'] == self.discriminator:\n                break\n        else:\n            raise PydanticUserError(\n                f'{source} needs a discriminator field for key {self.discriminator!r}', code='discriminator-no-field'\n            )\n        return self._infer_discriminator_values_for_field(field, source)\n\n    def _infer_discriminator_values_for_field(self, field: CoreSchemaField, source: str) -> list[str | int]:\n        if field['type'] == 'computed-field':\n            # This should never occur as a discriminator, as it is only relevant to serialization\n            return []\n        alias = field.get('validation_alias', self.discriminator)\n        if not isinstance(alias, str):\n            raise PydanticUserError(\n                f'Alias {alias!r} is not supported in a discriminated union', code='discriminator-alias-type'\n            )\n        if self._discriminator_alias is None:\n            self._discriminator_alias = alias\n        elif self._discriminator_alias != alias:\n            raise PydanticUserError(\n                f'Aliases for discriminator {self.discriminator!r} must be the same '\n                f'(got {alias}, {self._discriminator_alias})',\n                code='discriminator-alias',\n            )\n        return self._infer_discriminator_values_for_inner_schema(field['schema'], source)\n\n    def _infer_discriminator_values_for_inner_schema(\n        self, schema: core_schema.CoreSchema, source: str\n    ) -> list[str | int]:\n        \"\"\"When inferring discriminator values for a field, we typically extract the expected values from a literal\n        schema. This function does that, but also handles nested unions and defaults.\n        \"\"\"","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/pydantic/pydantic/blob/2e5f0e2b4218de31709f1cf9c5bc61ea97a68835/pydantic/_internal/_discriminated_union.py#L401-L437","documentation":"Pydantic raises this PydanticUserError (code 'discriminator-alias-type') when the discriminator field on a union member has a validation_alias that is not a plain string — for example an AliasChoices list, an AliasPath tuple, or any tuple/list-valued alias. The discriminated-union machinery at _infer_discriminator_values_for_field (line 417-421) reads field.get('validation_alias', self.discriminator) and requires it to be a str so it can be used as a single lookup key in the tagged-union schema. Complex alias paths cannot serve as a discriminator key.","triggerScenarios":"Annotating the discriminator field with Field(validation_alias=AliasPath('meta', 'kind')) or Field(validation_alias=AliasChoices('kind', 'type')) inside a member of a discriminated union. Also occurs when an alias_generator produces a non-string for the discriminator field name.","commonSituations":"Trying to read the discriminator value from a nested location in incoming JSON (e.g. AliasPath) rather than the top level. Reusing a model that has a generic alias generator that returns tuples for some fields.","solutions":["Use a plain string validation_alias on the discriminator field: Field(validation_alias='kind').","If the tag lives nested in your input, flatten the input before validation or use a pre-validator to hoist the value to the top level.","Remove the alias entirely so the discriminator field uses its Python attribute name as the lookup key.","Consider a callable Discriminator if the tag must be extracted via custom logic."],"exampleFix":"// before\nclass Cat(BaseModel):\n    pet_type: Literal['cat'] = Field(validation_alias=AliasPath('header', 'kind'))\n\n// after\nclass Cat(BaseModel):\n    pet_type: Literal['cat'] = Field(validation_alias='kind')","handlingStrategy":"validation","validationCode":"from pydantic.fields import AliasPath, AliasChoices\n\ndef check_discriminator_alias_simple(model_cls, field_name: str) -> None:\n    info = model_cls.model_fields[field_name]\n    alias = info.validation_alias\n    if alias is not None and not isinstance(alias, str):\n        raise TypeError(f'{model_cls.__name__}.{field_name} has non-string alias {alias!r}')\n\ncheck_discriminator_alias_simple(Cat, 'pet_type')","typeGuard":"def is_plain_alias(alias) -> bool:\n    return alias is None or isinstance(alias, str)","tryCatchPattern":"from pydantic import PydanticUserError\n\ntry:\n    Pet.model_rebuild()\nexcept PydanticUserError as e:\n    if e.code == 'discriminator-alias-type':\n        # replace AliasPath/AliasChoices on the discriminator with a plain str alias\n        ...","preventionTips":["Never use AliasPath or AliasChoices on the discriminator field — keep it a plain string alias.","If the tag is nested in input data, flatten it with a model_validator(mode='before') instead of an alias path.","Review alias_generator output to ensure it returns a str for the discriminator field."],"tags":["pydantic","discriminated-union","alias","schema-build"],"analyzedSha":"2e5f0e2b4218de31709f1cf9c5bc61ea97a68835","analyzedAt":"2026-08-04T19:54:21.281Z","schemaVersion":2}