Question: I am trying to validate Slack interaction payloads, that look like these: type: block_actions container: type: view ...  type: block_actions container: type: message ...  type: view_submission ...  I use 3 different models for payloads coming to the same interaction endpoint: class MessageContainer(BaseModel): type: Literal["message"] ... class ViewContainer(BaseModel): type: Literal["view"] ... class MessageActions(ActionsBase): type: Literal["block_actions"] container: MessageContainer ... class ViewActions(ActionsBase): type: Literal["block_actions"] container: ViewContainer ... class ViewSubmission(BaseModel): type: Literal["view_submission"] ...  and I was planning to use BlockActions = Annotated[ MessageActions | ViewActions, Field(discriminator="container.type"), ] SlackInteraction = Annotated[ ViewSubmission | BlockActions, Field(discriminator="type"), ] SlackInteractionAdapter = TypeAdapter(SlackInteraction)  but cannot make it work with v2.10.4. Do I have to dispatch them manually or there is a way to solve it with Pydantic?

Answer: Not sure it's possible to use 2 discriminators to resolve one type (as you are trying to do). I can suggest you 3 options: 1. Split block_actions into block_message_actions and block_view_actions: from typing import Annotated, Literal from pydantic import BaseModel, Field, TypeAdapter class MessageContainer(BaseModel): pass class ViewContainer(BaseModel): pass class ActionsBase(BaseModel): pass class MessageActions(ActionsBase): type: Literal["block_message_actions"] container: MessageContainer class ViewActions(ActionsBase): type: Literal["block_view_actions"] container: ViewContainer class ViewSubmission(BaseModel): type: Literal["view_submission"] SlackInteraction = Annotated[ ViewSubmission | ViewActions | MessageActions, Field(discriminator="type"), ] SlackInteractionAdapter = TypeAdapter(SlackInteraction) a = SlackInteractionAdapter.validate_python({"type": "view_submission"}) assert isinstance(a, ViewSubmission) b = SlackInteractionAdapter.validate_python( {"type": "block_message_actions", "container": {}}, ) assert isinstance(b, MessageActions) assert isinstance(b.container, MessageContainer) c = SlackInteractionAdapter.validate_python( {"type": "block_view_actions", "container": {}}, ) assert isinstance(c, ViewActions) assert isinstance(c.container, ViewContainer)  2. Use Discriminated Unions with callable Discriminator:  def get_discriminator_value(v: Any) -> str: if isinstance(v, dict): if v["type"] == "view_submission": return "view_submission" return "message_action" if v["container"]["type"] == "message" else "view_action" if v.type == "view_submission": return "view_submission" return "message_action" if v.container.type == "message" else "view_action" SlackInteraction = Annotated[ Union[ Annotated[ViewSubmission, Tag("view_submission")], Annotated[MessageActions, Tag("message_action")], Annotated[ViewActions, Tag("view_action")], ], Discriminator(get_discriminator_value), ] SlackInteractionAdapter = TypeAdapter(SlackInteraction)  3. Use nested discriminated unions: from typing import Annotated, Literal from pydantic import BaseModel, Field, TypeAdapter class MessageContainer(BaseModel): type: Literal["message"] class ViewContainer(BaseModel): type: Literal["view"] ActionContainer = Annotated[ MessageContainer | ViewContainer, Field(discriminator="type"), ] class BlockActions(BaseModel): type: Literal["block_actions"] container: ActionContainer class ViewSubmission(BaseModel): type: Literal["view_submission"] SlackInteraction = Annotated[ ViewSubmission | BlockActions, Field(discriminator="type"), ] SlackInteractionAdapter = TypeAdapter(SlackInteraction) a = SlackInteractionAdapter.validate_python({"type": "view_submission"}) assert isinstance(a, ViewSubmission) b = SlackInteractionAdapter.validate_python( {"type": "block_actions", "container": {"type": "message"}}, ) assert isinstance(b, BlockActions) assert isinstance(b.container, MessageContainer) c = SlackInteractionAdapter.validate_python( {"type": "block_actions", "container": {"type": "view"}}, ) assert isinstance(c, BlockActions) assert isinstance(c.container, ViewContainer)