Skip to content

Comments

fix: resolve PydanticSerializationUnexpectedValue warnings in responses.parse()#2885

Open
bledden wants to merge 2 commits intoopenai:mainfrom
bledden:fix/parsed-response-serialization-warnings
Open

fix: resolve PydanticSerializationUnexpectedValue warnings in responses.parse()#2885
bledden wants to merge 2 commits intoopenai:mainfrom
bledden:fix/parsed-response-serialization-warnings

Conversation

@bledden
Copy link

@bledden bledden commented Feb 22, 2026

Summary

Calling model_dump() or model_dump_json() on a ParsedResponse returned by responses.parse() emits ~19 PydanticSerializationUnexpectedValue warnings per call. This is because the runtime types (ParsedResponseOutputMessage[TypeVar], ParsedResponseOutputText[TypeVar]) don't match the declared union member types in the Pydantic schema, so the serializer tries every variant in the union and warns on each mismatch.

Root cause

The PropertyInfo(discriminator="type") metadata used on ParsedContent and ParsedResponseOutputItem is only used during deserialization (in construct_type()). Pydantic's serializer ignores it entirely, so it falls back to a plain union strategy that tries each variant sequentially. Combined with the unresolved TypeVar in the runtime-constructed objects, this produces a warning for every non-matching variant.

Fix

Wrapped the content and output fields on ParsedResponseOutputMessage and ParsedResponse with SerializeAsAny, which tells Pydantic to serialize using the runtime type's schema instead of the declared union member's schema. This only affects serialization — validation and deserialization are unchanged.

Before

>>> parsed.model_dump()
# 19 PydanticSerializationUnexpectedValue warnings

After

>>> parsed.model_dump()
# 0 warnings, correct serialized data including `parsed` field

Test plan

  • Bug reproduced locally by constructing a ParsedResponse from a dict (no API calls) and capturing warnings during model_dump()
  • Fix verified: 0 warnings after applying SerializeAsAny
  • Added 3 regression tests in tests/lib/responses/test_parsed_response_serialization.py
  • ruff check passes on all modified files
  • All new tests pass

Fixes #2872

…es.parse()

When calling model_dump() or model_dump_json() on a ParsedResponse
returned by responses.parse(), Pydantic emits ~19
PydanticSerializationUnexpectedValue warnings. This happens because the
runtime types (ParsedResponseOutputMessage[TypeVar],
ParsedResponseOutputText[TypeVar]) don't match the declared union
member types in the schema, so the serializer tries every variant and
warns on each mismatch.

The fix wraps the `output` and `content` fields with SerializeAsAny,
which tells Pydantic to serialize using the runtime type's schema
instead of the declared union member's schema. This only affects
serialization — validation and deserialization are unchanged.

Fixes openai#2872
@bledden bledden requested a review from a team as a code owner February 22, 2026 22:25
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4cd54235b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

from typing import TYPE_CHECKING, List, Union, Generic, TypeVar, Optional
from typing_extensions import Annotated, TypeAlias

from pydantic import SerializeAsAny

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve pydantic v1 compatibility for parsed response types

SerializeAsAny is a Pydantic v2-only symbol, so this unconditional import raises ImportError for environments still on Pydantic 1.x; that breaks importing openai.types.responses.parsed_response (and therefore responses.parse() paths) before any runtime fallback logic can run. The repo still carries explicit v1 compatibility (pydantic>=1.9.0,<3 in pyproject.toml and PYDANTIC_V1 branches in src/openai/_compat.py), so this change introduces a regression for those users.

Useful? React with 👍 / 👎.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already addressed in the current version of the PR. The SerializeAsAny import is guarded behind if not PYDANTIC_V1 (line 8-9), and both ParsedResponseOutputMessage and ParsedResponse use three-branch conditionals (TYPE_CHECKING / not PYDANTIC_V1 / else) so the symbol is never referenced on v1.

The unconditional `from pydantic import SerializeAsAny` import raises
ImportError on Pydantic v1, which the SDK still supports (pydantic>=1.9.0).
Guard the import behind PYDANTIC_V1 and add v1 fallback branches in the
class bodies that use the original undecorated type annotations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

responses.parse() throws a PydanticSerializationUnexpectedValue error in v2.21.0

1 participant