Skip to content

AgentLoader discards its own diagnosis: type mismatch on root_agent is logged at WARNING, then reported as "No root_agent found" #6606

Description

@aweussom

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).

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementationrequest clarification[Status] The maintainer need clarification or more information from the author

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions