When a module exposes a root_agent that is not a BaseAgent, AgentLoader detects the
exact problem, formats it with the offending type, writes it to a logger.warning, and
then falls through to raise an error describing something else entirely.
What the user sees
ValueError: No root_agent found for 'agent'. Searched in 'agent.agent.root_agent',
'agent.root_agent' and 'agent/root_agent.yaml'.
Expected directory structure:
<agents_dir>/
agent/
agent.py (with root_agent) OR
root_agent.yaml
Then run: adk web <agents_dir>
Ensure '/path/to/agents/judge' is structured correctly, an .env file can be loaded if
present, and a root_agent is exposed.
HINT: It looks like this command might be running from inside an agent directory. Run it
from the parent directory that contains your agent folder (for example the project root)
so the loader can locate your agents.
Every actionable statement in that message is wrong for this failure:
root_agent was found — it exists in the module.
- The directory structure was correct.
- The cwd was correct; the HINT sends you to restructure a working layout.
The actual cause
google/adk/cli/utils/agent_loader.py::_load_from_module_or_package (v1.28.0):
if hasattr(module_candidate, "app") and isinstance(module_candidate.app, App):
return module_candidate.app
elif hasattr(module_candidate, "root_agent"):
if isinstance(module_candidate.root_agent, BaseAgent):
return module_candidate.root_agent
else:
logger.warning(
"Root agent found is not an instance of BaseAgent. But a type %s",
type(module_candidate.root_agent),
)
The contract is that an App must be exported as app, and root_agent must be a
BaseAgent. My module did this:
root_agent = App(name="judge", root_agent=judge_agent) # wrong
app = App(name="judge", root_agent=judge_agent) # right
Reasonable mistake to make — App has a root_agent field, so assigning an App to a
module-level root_agent reads naturally, and the sample I was working from imports App
without showing the export name.
Reproduction
# agents/judge/agent.py
from google.adk.agents import Agent
from google.adk.apps.app import App
root_agent = App(name="judge", root_agent=Agent(name="judge", model="gemini-3-flash-preview"))
Load it with AgentLoader. At default log level the WARNING is invisible and you get the
directory-structure message.
Suggested fix
When the type-mismatch branch is taken, remember it and raise with that instead of the
generic not-found error. Roughly:
mismatched_type = None
...
else:
mismatched_type = type(module_candidate.root_agent)
logger.warning(...)
...
if mismatched_type is not None:
raise ValueError(
f"'{agent_name}.root_agent' is a {mismatched_type.__name__}, not a BaseAgent. "
f"If you meant to export an App, name it `app` instead of `root_agent`."
)
raise ValueError("No root_agent found ...")
The information is already computed; it is just being thrown away. Suppressing the
directory-structure diagram and the wrong-directory HINT in this case would also help,
since both actively mislead.
Impact
Low severity, high friction. Cost for one user was reading the loader source to discover a
naming contract. Anyone hitting it will plausibly restructure a correct directory tree
first, because that is what the error tells them to do.
Environment: google-adk 1.28.0, Python 3.12, Ubuntu 24.04 (WSL2).
When a module exposes a
root_agentthat is not aBaseAgent,AgentLoaderdetects theexact problem, formats it with the offending type, writes it to a
logger.warning, andthen falls through to raise an error describing something else entirely.
What the user sees
Every actionable statement in that message is wrong for this failure:
root_agentwas found — it exists in the module.The actual cause
google/adk/cli/utils/agent_loader.py::_load_from_module_or_package(v1.28.0):The contract is that an
Appmust be exported asapp, androot_agentmust be aBaseAgent. My module did this:Reasonable mistake to make —
Apphas aroot_agentfield, so assigning anAppto amodule-level
root_agentreads naturally, and the sample I was working from importsAppwithout showing the export name.
Reproduction
Load it with
AgentLoader. At default log level the WARNING is invisible and you get thedirectory-structure message.
Suggested fix
When the type-mismatch branch is taken, remember it and raise with that instead of the
generic not-found error. Roughly:
The information is already computed; it is just being thrown away. Suppressing the
directory-structure diagram and the wrong-directory HINT in this case would also help,
since both actively mislead.
Impact
Low severity, high friction. Cost for one user was reading the loader source to discover a
naming contract. Anyone hitting it will plausibly restructure a correct directory tree
first, because that is what the error tells them to do.
Environment: google-adk 1.28.0, Python 3.12, Ubuntu 24.04 (WSL2).