Skip to content

[BUG] PydanticSerializationUnexpectedValue warnings on every message_stop event when streaming #1715

Description

@lizradway

When using client.messages.stream(), calling model_dump() on the message_stop event produces PydanticSerializationUnexpectedValue warnings. this happens on every streamed response

Repro:

import warnings
from anthropic import Anthropic
from anthropic.types.parsed_message import ParsedTextBlock, ParsedMessage
from anthropic.lib.streaming._types import ParsedMessageStopEvent
from anthropic._models import build
import anthropic

# Simulates exactly what build_events() does for message_stop
parsed_msg = ParsedMessage.construct(
    id="msg_test", type="message", role="assistant",
    content=[ParsedTextBlock.construct(type="text", text="Hello!")],
    model="claude-sonnet-4-20250514", stop_reason="end_turn",
    usage=anthropic.types.Usage.construct(input_tokens=10, output_tokens=5),
)
event = build(ParsedMessageStopEvent, type="message_stop", message=parsed_msg)

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    event.model_dump()

print(f"{len(caught)} warnings")  # 1
print(caught[0].message)
# PydanticSerializationUnexpectedValue: Expected `ParsedTextBlock[TypeVar]` but got `ParsedTextBlock`...

Root cause:

In lib/streaming/_messages.py, build_events() constructs a ParsedMessageStopEvent with the accumulated ParsedMessage snapshot. ParsedMessageStopEvent inherits from a Generic, so its schema expects ParsedTextBlock[ResponseFormatT] in message.content. But build() uses construct_type which bypasses validation, stuffing an unparameterized ParsedTextBlock instance into that field. When Pydantic serializes it, it can't match the concrete instance against the generic type parameter and warns.

Only message_stop is affected as other event types don't carry ParsedMessage.content through serialization.
It looks like this may have been introduced in ad56677 and only shows up at serialization time rather than typechecking or validation, likely explaining why this was not caught.

Affected versions: anthropic 0.84.0 through 0.113.0 (latest), all pydantic 2.x versions. Tested:

  • anthropic 0.84.0 + pydantic 2.9.2 ✗
  • anthropic 0.90.0 + pydantic 2.9.2 ✗
  • anthropic 0.100.0 + pydantic 2.9.2 ✗
  • anthropic 0.113.0 + pydantic 2.9.2 ✗
  • anthropic 0.113.0 + pydantic 2.10.0 ✗
  • anthropic 0.113.0 + pydantic 2.11.0 ✗
  • anthropic 0.113.0 + pydantic 2.12.0 ✗
  • anthropic 0.113.0 + pydantic 2.13.4 ✗

Related: #1175, #1422

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions