Skip to content

Part converter drops Part(text='') causing broken A2A responses #5341

@luis5tb

Description

@luis5tb

Description

convert_genai_part_to_a2a_part() in src/google/adk/a2a/converters/part_converter.py uses a truthiness check on part.text:

if part.text:  # line 182
    a2a_part = a2a_types.TextPart(text=part.text)
    ...

In Python, '' (empty string) is falsy, so a Part(text='') falls through this check and every subsequent type check (file_data, inline_data, function_call, etc.), reaching the catch-all warning at line 298:

Cannot convert unsupported part for Google GenAI part: media_resolution=None code_execution_result=None executable_code=None file_data=None function_call=None function_response=None inline_data=None text='' thought=None thought_signature=None video_metadata=None tool_call=None tool_response=None part_metadata=None

The converter returns None, the part is dropped, and when all parts in a response are empty the A2A message ends up with zero parts — the client sees "broken thinking" with no actual response.

Root Cause

Part(text='') is produced in at least two places in the ADK itself:

  1. code_executors/code_execution_utils.py — when code execution completes with None output:

    content.parts[-1] = types.Part(text='')
  2. models/interactions_utils.py — when the Interactions API returns a text output with None content:

    return types.Part.from_text(text=output.text or '')

Gemini 2.5 Flash (thinking model) also appears to produce Part(text='') in some thinking-mode responses.

Proposed Fix

Change line 182 from:

if part.text:

to:

if part.text is not None:

This preserves the existing behavior for text=None (skip) while correctly handling text='' as a valid text part.

Impact

This affects any A2A integration using a thinking model (e.g., Gemini 2.5 Flash/Pro) where the model occasionally returns empty text parts. The user experience is a "broken thinking" indicator with no response content.

Workaround

We implemented an after_model_callback plugin that strips Part(text='') entries before they reach the converter and returns a fallback message when all parts are empty. This prevents the broken state but cannot transparently retry the model call since the ADK plugin API doesn't support re-invocation from after_model_callback.

Environment

  • google-adk installed from PyPI (version in use with Python 3.12/3.13)
  • Model: gemini-2.5-flash
  • Transport: A2A protocol (JSON-RPC / SSE)

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementation

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions