Two Nasty Gotchas When Building Multi-Agent Systems with Google ADK

Published: (April 28, 2026 at 05:30 AM EDT)
3 min read
Source: Dev.to

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’s agent_loader calls importlib.import_module(agent_name) on every request.
  • On the first request the module is loaded, root_agent is created, and each sub‑agent gets parent_agent = root_agent.
  • On the second request the module is re‑imported, but the module‑level singletons sub_agent_a and sub_agent_b are the same Python objects from the previous load, still holding their old parent_agent.
  • When the new LlmAgent tries to assign parent_agent again, 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

BugTriggerFix
Parent‑agent collisionModule‑level singleton sub‑agents + ADK module reload per requestReset agent.parent_agent = None before passing sub‑agents to the constructor
Context variable not found{word} patterns in instruction stringsUse backticks, [word] (or other non‑brace delimiters) for literal placeholders
0 views
Back to Blog

Related posts

Read more »