Skip to content

Template variable escaping with double braces doesn't work as documented #3527

Description

@j0monty

Description

The ADK documentation states that double braces ({{ and }}) should be used to escape literal curly braces in agent instructions, but the implementation in instructions_utils.py does not properly handle this escaping mechanism.

Expected Behavior

According to the State documentation:

If your instruction text legitimately needs to include literal curly braces (e.g., as part of a code example or JSON snippet), you need to escape them... a common approach is doubling the braces: {{ becomes { and }} becomes }.

When using {{variable_name}} in an instruction, it should render as {variable_name} without attempting template substitution.

Actual Behavior

The regex pattern {+[^{}]*}+ in inject_session_state() matches {{variable_name}} and attempts to substitute it from session state, causing a KeyError when the variable doesn't exist.

The issue is in these lines of google/adk/utils/instructions_utils.py:

Line 124: Regex matches {{variable}}

return await _async_sub(r'{+[^{}]*}+', _replace_match, template)

Lines 82-83: Strips all braces, leaving just 'variable'

async def _replace_match(match) -> str:
var_name = match.group().lstrip('{').rstrip('}').strip()

Line 143: 'variable' passes identifier check

return var_name.isidentifier()

Lines 109-122: Tries to inject from state and fails

if var_name in invocation_context.session.state:
...
else:
raise KeyError(f'Context variable not found: {var_name}.')## Steps to Reproduce

  1. Create an agent with an instruction containing code examples with template literals:
    instruction = """
    Example Python code:
    logger.error(f"User not found: {{user_id}}")

Example TypeScript code:
console.log(User: ${{userId}});
"""2. Run the agent
3. Observe: KeyError: 'Context variable not found: 'user_id'.

Environment

  • ADK Version: 1.18.0 (tested with google-adk>=1.18.0)
  • Python Version: 3.12
  • OS: macOS 24.6.0

Workaround

Use property access (dots) in code examples instead of simple identifiers, since _is_valid_state_name() returns False for non-identifiers:

This works because 'user.id' is not a valid identifier

logger.error(f"User not found: {{user.id}}")## Suggested Fix

The _replace_match function should detect double-brace patterns and return the literal content without attempting substitution:

async def _replace_match(match) -> str:
matched_text = match.group()

# Check if this is an escaped pattern (starts with {{ )
if matched_text.startswith('{{') and matched_text.endswith('}}'):
    # Return with single braces (unescape)
    return matched_text[1:-1]

# Existing logic for template substitution
var_name = matched_text.lstrip('{').rstrip('}').strip()
...Alternatively, update the regex to exclude double-brace patterns from matching, though this is more complex.

Impact

This bug prevents using realistic code examples (especially Python f-strings and TypeScript/JavaScript template literals) in agent instructions, which is critical for code generation and software engineering agents.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

core[Component] This issue is related to the core interface and implementationgood first issue[Community] This issue is good for newcomers to participatetools[Component] This issue is related to tools

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions