Skip to content

Regression in v1.0.3: UnexpectedModelBehavior crash during streaming with model reasoning #2862

@Joakim-T

Description

@Joakim-T

Initial Checks

Description

Related to issue #2687 and PR #2855

I've been digging into an issue I ran into after upgrading from (my very slightly modified) v1.0.2 to v1.0.3.

It seems there's a regression that causes a crash during streaming whenever the model includes a reasoning step in its response via the OpenAI Responses API.
The specific error I'm seeing is pydantic_ai.exceptions.UnexpectedModelBehavior: Cannot create a ThinkingPart with no content.

What's Happening

It looks like the crash is triggered by new logic introduced in v1.0.3 to handle the ResponseOutputItemDoneEvent for a ResponseReasoningItem.

Here's what I've traced so far:

  1. In pydantic_ai/models/openai.py, inside the OpenAIResponsesStreamedResponse._get_event_iterator method, there's a new block for ResponseOutputItemDoneEvent

warnings.warn( # pragma: no cover
f'Handling of this item type is not yet implemented. Please report on our GitHub: {chunk}',
UserWarning,
)
elif isinstance(chunk, responses.ResponseOutputItemDoneEvent):
if isinstance(chunk.item, responses.ResponseReasoningItem):
# Add the signature to the part corresponding to the first summary item
signature = chunk.item.encrypted_content
yield self._parts_manager.handle_thinking_delta(
vendor_part_id=f'{chunk.item.id}-0',
id=chunk.item.id,
signature=signature,
provider_name=self.provider_name if signature else None,
)
pass

  1. When this event is for a ResponseReasoningItem, the new code calls self._parts_manager.handle_thinking_delta, passing in a signature but leaving the content argument as None.

  2. The problem is that in pydantic_ai/_parts_manager.py, the handle_thinking_delta function has a strict check. If it tries to create a new ThinkingPart (because one doesn't already exist for that vendor_part_id), it requires the content to not be None ([around line 210 in the 1.0.3 release]

)
self._parts[part_index] = part_delta.apply(existing_thinking_part)
return PartDeltaEvent(index=part_index, delta=part_delta)
else:
raise UnexpectedModelBehavior('Cannot update a ThinkingPart with no content or signature')
def handle_tool_call_delta(
self,

This mismatch leads to the UnexpectedModelBehavior exception, which stops the stream. This doesn't happen in v1.0.2 because that version doesn't have the code to handle the ResponseOutputItemDoneEvent, so the problematic call is never made.

Possible Fix

It seems like the fix might be to either:

  • Make the validation in handle_thinking_delta a bit more flexible, allowing a ThinkingPart to be created with just a signature.
  • Or, ensure that a ThinkingPart is always created with some initial content before the ResponseOutputItemDoneEvent is processed, so the call becomes an update rather than a creation.

Let me know if you need any more info.

Example Code

Python, Pydantic AI & LLM client version

pydantic-ai-slim==1.0.3
openai==1.107.1
ag-ui-protocol==0.1.8

Metadata

Metadata

Assignees

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