Skip to content

adk session: KeyError: 'Context variable not found: expression' when using adk run with A2UI agents #1388

@iyedb

Description

@iyedb

Description

When running an A2UI-enabled agent using adk run, the CLI crashes with a KeyError as soon as the first user message is sent.

KeyError: 'Context variable not found: `expression`.'

Full traceback excerpt:

File ".../google/adk/utils/instructions_utils.py", line 124, in inject_session_state
    return await _async_sub(r'{+[^{}]*}+', _replace_match, template)
  File ".../google/adk/utils/instructions_utils.py", line 122, in _replace_match
    raise KeyError(f'Context variable not found: `{var_name}`.')
KeyError: 'Context variable not found: `expression`.'

Steps to Reproduce

  1. Create an ADK agent that uses A2uiSchemaManager.generate_system_prompt() with include_schema=True to build its instruction.

  2. Expose root_agent at module level so it is compatible with adk run.

    # agent.py
    root_agent = LlmAgent(
        model=Gemini(model="gemini-2.0-flash"),
        name="my_agent",
        instruction=schema_manager.generate_system_prompt(
            role_description="...",
            include_schema=True,
            include_examples=True,
        ),
        tools=[...],
    )
  3. Run the agent:

    adk run path/to/my_agent
  4. Type any message and press Enter → crash.


Root Cause

The BasicCatalog JSON schema bundled at agent_sdks/python/src/a2ui/assets/0.9/basic_catalog.json contains the following text in the description of the formatString function:

The value string can contain interpolated expressions in the `${expression}` format.

When generate_system_prompt() is called with include_schema=True, this description is embedded verbatim into the agent's instruction string.

When adk run processes the first user message, the ADK framework calls inject_session_state() in instructions_utils.py. This function scans the full instruction string with the regex r'{+[^{}]*}+'. It matches {expression}, strips the braces to extract "expression", validates it as a Python identifier (which passes), looks it up in the active session state dict (which is empty because adk run manages sessions internally), and raises a KeyError.

The key line in the ADK source:

# google/adk/utils/instructions_utils.py
return await _async_sub(r'{+[^{}]*}+', _replace_match, template)

Why Existing A2A Server Samples Are Unaffected

The existing A2A-based samples (e.g. samples/agent/adk/restaurant_finder/) manually create the ADK session and inject a dummy variable before running:

# restaurant_finder/agent.py
session_state = {"base_url": self.base_url, "expression": "{expression}"}
session = await runner.session_service.create_session(
    ...,
    state=session_state,
)

This satisfies the ADK template engine — it finds "expression" in the state and silently replaces the tag. adk run manages sessions internally and provides no hook to pre-seed the session state before the first message, so this workaround is not available when using the CLI.


Metadata

Metadata

Assignees

Labels

type: bugSomething isn't working

Type

No type
No fields configured for issues without a type.

Projects

Status

In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions