🔴 Required Information
Describe the Bug:
GET /apps/{app_name}/app-info returns 500 Internal Server Error for any app whose root or sub-agent defines instruction as an InstructionProvider (a callable) instead of a plain string. AgentInfo in google/adk/utils/agent_info.py types the field as instruction: str and assigns current_agent.instruction verbatim, so building the response raises an unhandled pydantic.ValidationError. Callable instructions are a documented, first-class feature (Callable[[ReadonlyContext], str | Awaitable[str]]) and the agent runs fine at runtime — only the app-info introspection endpoint crashes.
Steps to Reproduce:
- Create an agent whose
instruction is a callable:
# agents/dyn/agent.py
from google.adk.agents import LlmAgent
from google.adk.agents.readonly_context import ReadonlyContext
def dynamic_instruction(ctx: ReadonlyContext) -> str:
return "You are a helpful assistant."
root_agent = LlmAgent(
name="dyn",
model="gemini-3.7-flash", # any model; never reached
description="Agent with a dynamic (callable) instruction.",
instruction=dynamic_instruction, # callable, not a str
)
- Run
adk web agents
- Request
curl http://localhost:8000/apps/dyn/app-info
- Receive
500 Internal Server Error (stack trace below)
Expected Behavior:
app-info should succeed for agents with callable instructions — returning a serialized AgentInfo where instruction is coerced/resolved to a string (or a readable placeholder) rather than raising.
Observed Behavior:
500, with:
File ".../google/adk/cli/api_server.py", line 1416, in get_adk_app_info
agents=await get_agents_dict(root_agent),
File ".../google/adk/utils/agent_info.py", line 71, in _traverse
agents_dict[current_agent.name] = AgentInfo(
File ".../pydantic/main.py", line 263, in __init__
validated_self = self.__pydantic_validator__.validate_python(...)
pydantic_core._pydantic_core.ValidationError: 1 validation error for AgentInfo
instruction
Input should be a valid string [type=string_type,
input_value=<function dynamic_instruction at 0x...>, input_type=function]
Environment Details:
- ADK Library Version (pip show google-adk): 2.7.1
- Desktop OS: Linux (Ubuntu, Docker container)
- Python Version (python -V): 3.11
Model Information:
- Are you using LiteLLM: N/A — model-independent; the model is never reached (validation fails while building
app-info, before any inference). Reproduces regardless of provider.
- Which model is being used: N/A (any; e.g.
gemini-3.7-flash)
🟡 Optional Information
Regression:
Not a regression — broken since the app-info endpoint was introduced (commit da438fafd, 2026-04-06). AgentInfo.instruction has been typed str from that first commit and is still str on main, so no released version handles callable instructions here.
Logs:
pydantic_core._pydantic_core.ValidationError: 1 validation error for AgentInfo
instruction
Input should be a valid string [type=string_type,
input_value=<function dynamic_instruction at 0x...>, input_type=function]
For further information visit https://errors.pydantic.dev/2.13/v/string_type
Screenshots / Video:
N/A
Additional Context:
Root cause in google/adk/utils/agent_info.py:
class AgentInfo(pydantic.BaseModel):
...
instruction: str # LlmAgent.instruction is Union[str, InstructionProvider]
# get_agents_dict._traverse():
agents_dict[current_agent.name] = AgentInfo(
...
instruction=current_agent.instruction, # may be a callable -> ValidationError
)
Suggested fix — coerce a callable before validation:
@pydantic.field_validator("instruction", mode="before")
@classmethod
def _coerce_callable_instruction(cls, v):
if callable(v):
return f"<InstructionProvider: {getattr(v, '__name__', repr(v))}>"
return v
(Alternatively, resolve the provider when a context is available and fall back to a placeholder — note an InstructionProvider may be async and may require session state, so eager resolution isn't always safe here.)
Distinct from the invalid-app_name 500 fixed by #5374 / #5376 / #6376 — this is a valid app whose callable instruction fails validation. Separately, get_agents_dict only traverses isinstance(sub_agent, LlmAgent), so non-LlmAgent sub-agents (e.g. RemoteA2aAgent) are silently omitted from agents/sub_agents — expected, but worth noting for anyone using app-info as an inventory.
Minimal Reproduction Code:
from google.adk.agents import LlmAgent
from google.adk.agents.readonly_context import ReadonlyContext
def dynamic_instruction(ctx: ReadonlyContext) -> str:
return "You are a helpful assistant."
root_agent = LlmAgent(
name="dyn",
model="gemini-3.7-flash",
description="Agent with a dynamic (callable) instruction.",
instruction=dynamic_instruction,
)
# adk web agents -> GET /apps/dyn/app-info -> 500
How often has this issue occurred?:
- Always (100%) — deterministic for any callable instruction.
🔴 Required Information
Describe the Bug:
GET /apps/{app_name}/app-inforeturns 500 Internal Server Error for any app whose root or sub-agent definesinstructionas anInstructionProvider(a callable) instead of a plain string.AgentInfoingoogle/adk/utils/agent_info.pytypes the field asinstruction: strand assignscurrent_agent.instructionverbatim, so building the response raises an unhandledpydantic.ValidationError. Callable instructions are a documented, first-class feature (Callable[[ReadonlyContext], str | Awaitable[str]]) and the agent runs fine at runtime — only theapp-infointrospection endpoint crashes.Steps to Reproduce:
instructionis a callable:adk web agentscurl http://localhost:8000/apps/dyn/app-info500 Internal Server Error(stack trace below)Expected Behavior:
app-infoshould succeed for agents with callable instructions — returning a serializedAgentInfowhereinstructionis coerced/resolved to a string (or a readable placeholder) rather than raising.Observed Behavior:
500, with:
Environment Details:
Model Information:
app-info, before any inference). Reproduces regardless of provider.gemini-3.7-flash)🟡 Optional Information
Regression:
Not a regression — broken since the
app-infoendpoint was introduced (commitda438fafd, 2026-04-06).AgentInfo.instructionhas been typedstrfrom that first commit and is stillstronmain, so no released version handles callable instructions here.Logs:
Screenshots / Video:
N/A
Additional Context:
Root cause in
google/adk/utils/agent_info.py:Suggested fix — coerce a callable before validation:
(Alternatively, resolve the provider when a context is available and fall back to a placeholder — note an
InstructionProvidermay be async and may require session state, so eager resolution isn't always safe here.)Distinct from the invalid-
app_name500 fixed by #5374 / #5376 / #6376 — this is a valid app whose callable instruction fails validation. Separately,get_agents_dictonly traversesisinstance(sub_agent, LlmAgent), so non-LlmAgentsub-agents (e.g.RemoteA2aAgent) are silently omitted fromagents/sub_agents— expected, but worth noting for anyone usingapp-infoas an inventory.Minimal Reproduction Code:
How often has this issue occurred?: