fix: 'NoneType' object is not iterable

Merge https://github.com/google/adk-python/pull/3988

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

- Closes: https://github.com/google/adk-python/issues/3987
- Related: https://github.com/google/adk-python/issues/3596

**2. Or, if no issue exists, describe the change:**

**Problem:**
Idea about my use case
I'm building the report generation system using google-ask (1.18.0) and building multiple subagents here, I'm passing the one subagent Agent as a tool to another Parent Agent. Note: Sub-agent can do web search.
here, parent agent triggers multiple sub-agent (same agent) multiple times according to use case or complexity of the user input

Describe the bug
here, the bug sometimes sub agents doesn't provide the proper output and resulted in the
```
merged_text = '\n'.join(p.text for p in last_content.parts if p.text)
^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not iterable
and it's breaking the system of Agents workflow

```

**Solution:**
Creating fallback if there is no **last_content.parts** it will return the empty parts so we won't face the NoneType issue

### Testing Plan
Created a unit test file for this issue
test_google_search_agent_tool_repro.py

**Unit Tests:**

- [X] I have added or updated unit tests for my change.
- [X] All unit tests pass locally.

_Please include a summary of passed `pytest` results._
3677 passed, 2208 warnings in 42.64s

**Manual End-to-End (E2E) Tests:**
N/A

### Checklist

- [X] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [X] I have performed a self-review of my own code.
- [X] I have commented my code, particularly in hard-to-understand areas.
- [X] I have added tests that prove my fix is effective or that my feature works.
- [X] New and existing unit tests pass locally with my changes.
- [X] I have manually tested my changes end-to-end.
- [ ] Any dependent changes have been merged and published in downstream modules.

### Additional context
N/A

Co-authored-by: Liang Wu <wuliang@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3988 from ananthanarayanan-28:none-type-issue e6ba948345adfc5ac73a5e39d11c68236f117179
PiperOrigin-RevId: 856515019
This commit is contained in:
Anantha Narayanan
2026-01-14 23:32:53 -08:00
committed by Copybara-Service
parent 2ed686527a
commit 7db3ce9613
3 changed files with 44 additions and 2 deletions
+1 -1
View File
@@ -207,7 +207,7 @@ class AgentTool(BaseTool):
# to avoid "Attempted to exit cancel scope in a different task" errors
await runner.close()
if not last_content:
if last_content is None or last_content.parts is None:
return ''
merged_text = '\n'.join(
p.text for p in last_content.parts if p.text and not p.thought
@@ -123,7 +123,7 @@ class GoogleSearchAgentTool(AgentTool):
last_content = event.content
last_grounding_metadata = event.grounding_metadata
if not last_content:
if last_content is None or last_content.parts is None:
return ''
merged_text = '\n'.join(p.text for p in last_content.parts if p.text)
if isinstance(self.agent, LlmAgent) and self.agent.output_schema:
+42
View File
@@ -900,3 +900,45 @@ def test_agent_tool_with_input_schema_uses_json_schema_feature(
},
'response_json_schema': {'type': 'object'},
}
@mark.asyncio
async def test_run_async_handles_none_parts_in_response():
"""Verify run_async handles None parts in response without raising TypeError."""
# Mock model for the tool_agent that returns content with parts=None
# This simulates the condition causing the TypeError
tool_agent_model = testing_utils.MockModel.create(
responses=[
LlmResponse(
content=types.Content(parts=None),
)
]
)
tool_agent = Agent(
name='tool_agent',
model=tool_agent_model,
)
agent_tool = AgentTool(agent=tool_agent)
session_service = InMemorySessionService()
session = await session_service.create_session(
app_name='test_app', user_id='test_user'
)
invocation_context = InvocationContext(
invocation_id='invocation_id',
agent=tool_agent,
session=session,
session_service=session_service,
)
tool_context = ToolContext(invocation_context=invocation_context)
# This should not raise `TypeError: 'NoneType' object is not iterable`.
tool_result = await agent_tool.run_async(
args={'request': 'test request'}, tool_context=tool_context
)
assert tool_result == ''