Skip to content

Rare Assertion error when using Gemini Models in streaming mode #2609

@amiyapatanaik

Description

@amiyapatanaik

Initial Checks

Description

For a long time, random assertion errors continued to happen for complex agents using Gemini models in streaming mode. It is rare but happens enough times to make the system unreliable. The error as logged in Logfire before the 0.7.2 update was consistently:

Traceback (most recent call last):
  File "/opt/venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py", line 589, in use_span
    yield span
  File "/opt/venv/lib/python3.12/site-packages/pydantic_graph/graph.py", line 260, in iter
    yield GraphRun[StateT, DepsT, RunEndT](
  File "/opt/venv/lib/python3.12/site-packages/pydantic_ai/agent.py", line 754, in iter
    yield agent_run
  File "/opt/venv/lib/python3.12/site-packages/nxd/chat.py", line 198, in handle_agent_streaming
    async for event in request_stream:
  File "/opt/venv/lib/python3.12/site-packages/pydantic_ai/result.py", line 162, in aiter
    async for event in usage_checking_stream:
  File "/opt/venv/lib/python3.12/site-packages/pydantic_ai/models/google.py", line 455, in _get_event_iterator
    assert candidate.content.parts is not None
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

The 0.7.2 patch did not solve the issue:

Traceback (most recent call last):
  File "/opt/venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py", line 589, in use_span
    yield span
  File "/opt/venv/lib/python3.12/site-packages/pydantic_graph/graph.py", line 259, in iter
    yield GraphRun[StateT, DepsT, RunEndT](
  File "/opt/venv/lib/python3.12/site-packages/pydantic_ai/agent/__init__.py", line 661, in iter
    yield agent_run
  File "/opt/venv/lib/python3.12/site-packages/nxd/chat.py", line 198, in handle_agent_streaming
    async for event in request_stream:
  File "/opt/venv/lib/python3.12/site-packages/pydantic_ai/result.py", line 441, in _iterator
    async for item in stream_response:
  File "/opt/venv/lib/python3.12/site-packages/pydantic_ai/models/__init__.py", line 575, in iterator_with_final_event
    async for event in iterator:
  File "/opt/venv/lib/python3.12/site-packages/pydantic_ai/models/google.py", line 532, in _get_event_iterator
    assert candidate.content.parts is not None
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError

The Root Cause

  • Streaming API Behavior: When you stream a response from a model like Gemini, you get a series of "chunks." Most of these chunks contain content deltas (e.g., the next few words of text). The very last chunk, however, often contains no new content but instead provides final metadata, such as the finish_reason (e.g., "STOP", "MAX_TOKENS") and the final usage_metadata.
  • The Problematic Chunk: It appears that this final chunk from the Gemini API can be structured with a candidate.content object that exists, but its parts attribute is None. This signifies "there is a content block, but it has no new parts in this specific chunk."

This is separate from the issue when sometime Gemini models stream thoughts and then forget to actually stream the answer #2469 .

My suggestion is to change:

# ... inside _get_event_iterator
            assert candidate.content.parts is not None
            for part in candidate.content.parts:
                if part.text is not None:
# ...

to

# ... inside _get_event_iterator
            for part in candidate.content.parts or []:
                if part.text is not None:
# ...

Interestingly, the non-streaming response handler already does it this way:

# in GoogleModel._process_response (for non-streaming)

parts = response.candidates[0].content.parts or []
# This is the correct, safe way to handle it.

Example Code

Python, Pydantic AI & LLM client version

All Gemini Models

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions