Skip to content

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

@j0monty

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.

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