Skip to content

Pydantic converter with ToJsonOptions can't read back aliased models (pydantic >= 2.11) #2049

Closed

Description

@sohom-cs

With ToJsonOptions set, PydanticJSONPlainPayloadConverter.to_payload serializes with
SchemaSerializer.to_json(value, exclude_unset=...), and from_payload validates with TypeAdapter.validate_json, which reads aliases. Since pydantic-core 2.33 (pydantic 2.11), SchemaSerializer.to_json no longer defaults to by_alias=True: it follows the model's serialize_by_alias, which is False by default. So on this path aliased fields are written under their Python names and then can't be read back. Without ToJsonOptions the converter uses pydantic_core.to_json, which still writes aliases, so only the options path is affected.

What happens:

  • A model with Field(alias=...) or alias_generator=to_camel fails to decode. As an activity argument that is
    "Failed decoding arguments", a retryable ApplicationError, so it retries until schedule-to-close.
  • A model with extra="allow" decodes, but the aliased field is silently lost (for example the OpenAI SDK's
    ResponseFunctionToolCall.async_ becomes None).

Who reaches it: anyone who sets ToJsonOptions, and every user of the Deep Agents, Google ADK and OpenAI Agents plugins in temporalio/ai-integrations, which each install a converter with ToJsonOptions(exclude_unset=True).

Repro (server-free): a model Order(order_id: str = Field(alias="orderId")) round-tripped through
PydanticPayloadConverter with ToJsonOptions(exclude_unset=True) raises ValidationError on pydantic 2.11+; with pydantic 2.10.6 it round-trips. pydantic_core.to_json writes orderId on all versions.

Proposed fix: pass by_alias=True on the options path, which matches to_json. I have a PR with a test ready.

Activity

  1. sohom-cs commented on Oct 30, 2026

    @sohom-cs
    Author

    Closing: #2039 (merged 10-29) fixes this. It writes aliases on the ToJsonOptions path, and its test round-trips an aliased model with exclude_unset=True. I missed it before filing, sorry for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions