Two Nasty Gotchas When Building Multi-Agent Systems with Google ADK
Source: Dev.to
Parent‑agent collision
What happened
# Agents/my_app/root_agent.py
from Agents.my_app.sub_agent_a.agent import sub_agent_a
from Agents.my_app.sub_agent_b.agent import sub_agent_b
def _build_sub_agents() -> list:
return [sub_agent_a, sub_agent_b]
root_agent = LlmAgent(
name="my_app",
sub_agents=_build_sub_agents(),
# …
)
- Works locally with
adk web. - Crashes on Cloud Run with:
pydantic_core._pydantic_core.ValidationError: 1 validation error for LlmAgent
Value error, Agent `SubAgentA` already has a parent agent,
current parent: `my_app`, trying to add: `my_app`
Why it occurs
ADK’sagent_loadercallsimportlib.import_module(agent_name)on every request.- On the first request the module is loaded,
root_agentis created, and each sub‑agent getsparent_agent = root_agent. - On the second request the module is re‑imported, but the module‑level singletons
sub_agent_aandsub_agent_bare the same Python objects from the previous load, still holding their oldparent_agent. - When the new
LlmAgenttries to assignparent_agentagain, the validator raises the error.
Simplified snippet from LlmAgent.__init__:
for sub in sub_agents:
if sub.parent_agent is not None:
raise ValueError(f"Agent `{sub.name}` already has a parent agent ...")
sub.parent_agent = self
Fix
Reset the parent_agent reference before constructing a new root agent:
def _build_sub_agents() -> list:
agents = [sub_agent_a, sub_agent_b]
for agent in agents:
agent.parent_agent = None # reset before each reload
return agents
The assignment is synchronous and safe because the new parent is set immediately afterward.
Context variable not found in instruction strings
What happened
KeyError: 'Context variable not found: `hostname`.'
The traceback points to google/adk/utils/instructions_utils.py where ADK injects session state into instruction strings.
Why it occurs
ADK scans instruction text with the regex r'{+[^{}]*}+' and replaces every {var_name} with the corresponding session‑state value. Any pattern that looks like {word} is treated as a variable, even if you intend it to be a literal placeholder.
Example instruction:
The URL format is `https://{hostname}/api/{resource_id}/`
ADK tries to resolve {hostname} and {resource_id}; if they are not present in the session state, a KeyError is raised.
Escaping with double braces ({{hostname}}) does not work because the regex still matches the pattern.
Fix
Avoid using curly braces for literal template text in instructions. Use a different delimiter such as angle brackets, square brackets, or plain prose:
The URL format is `https:///api//`
Any {word} pattern will be interpreted as a session variable, so replace it with backticks, [word], or another non‑brace delimiter when you need a placeholder that should not be resolved.
Summary of the two gotchas
| Bug | Trigger | Fix |
|---|---|---|
| Parent‑agent collision | Module‑level singleton sub‑agents + ADK module reload per request | Reset agent.parent_agent = None before passing sub‑agents to the constructor |
| Context variable not found | {word} patterns in instruction strings | Use backticks, [word] (or other non‑brace delimiters) for literal placeholders |