Skip to content

Commit 86b2e5b

Browse files
DeanChensjcopybara-github
authored andcommitted
fix(utils): include agent name in instruction context variable missing error
When instructions rendering fails due to a missing context variable or artifact, include the agent name in the KeyError message so developers can identify which agent failed in multi-agent workflows. Co-authored-by: Shangjie Chen <deanchen@google.com> PiperOrigin-RevId: 973901646
1 parent 5e6df42 commit 86b2e5b

3 files changed

Lines changed: 37 additions & 7 deletions

File tree

docs/guides/workflow/parallel_worker/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ analyzer_agent = Agent(
6565

6666
Do not write `{node_input}` in the instruction. Braces in an instruction are
6767
substituted from session state, and there is no state key by that name, so the
68-
agent fails with ``KeyError: 'Context variable not found: `node_input`.'``. If you
68+
agent fails with ``KeyError: "Context variable not found: `node_input` in agent 'analyzer'."``. If you
6969
want a value from state in the instruction, put it in state first. The parallel
7070
worker sample does exactly that, writing `topic` in an earlier node and reading
7171
`{topic}` in the agent.

src/google/adk/utils/instructions_utils.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,10 @@ async def _replace_match(match) -> str:
153153
)
154154
return ''
155155
else:
156-
raise KeyError(f'Artifact {var_name} not found.')
156+
raise KeyError(
157+
f"Artifact '{var_name}' not found in agent"
158+
f" '{readonly_context.agent_name}'."
159+
)
157160
return str(artifact)
158161
else:
159162
if not _is_valid_state_name(var_name):
@@ -171,7 +174,10 @@ async def _replace_match(match) -> str:
171174
)
172175
return ''
173176
else:
174-
raise KeyError(f'Context variable not found: `{var_name}`.')
177+
raise KeyError(
178+
f'Context variable not found: `{var_name}` in agent'
179+
f" '{readonly_context.agent_name}'."
180+
)
175181

176182
return await _async_sub(_TEMPLATE_VAR_PATTERN, _replace_match, template)
177183

@@ -219,7 +225,10 @@ async def _load_artifact(filename: str) -> str:
219225
filename=filename,
220226
)
221227
if artifact is None:
222-
raise KeyError(f'Artifact {filename} not found.')
228+
raise KeyError(
229+
f"Artifact '{filename}' not found in agent"
230+
f" '{readonly_context.agent_name}'."
231+
)
223232
return str(artifact)
224233

225234
env = jinja2.Environment(

tests/unittests/utils/test_instructions_utils.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ async def test_inject_session_state_with_missing_state_raises_key_error():
129129
)
130130

131131
with pytest.raises(
132-
KeyError, match="Context variable not found: `missing_key`."
132+
KeyError,
133+
match=r"Context variable not found: `missing_key` in agent 'agent'\.",
133134
):
134135
await instructions_utils.inject_session_state(
135136
instruction_template, invocation_context
@@ -146,7 +147,9 @@ async def test_inject_session_state_with_missing_artifact_raises_key_error():
146147
artifact_service=mock_artifact_service
147148
)
148149

149-
with pytest.raises(KeyError, match="Artifact missing_file not found."):
150+
with pytest.raises(
151+
KeyError, match=r"Artifact 'missing_file' not found in agent 'agent'\."
152+
):
150153
await instructions_utils.inject_session_state(
151154
instruction_template, invocation_context
152155
)
@@ -232,7 +235,9 @@ async def test_inject_session_state_with_empty_artifact_name_raises_key_error():
232235
artifact_service=mock_artifact_service
233236
)
234237

235-
with pytest.raises(KeyError, match="Artifact not found."):
238+
with pytest.raises(
239+
KeyError, match=r"Artifact '' not found in agent 'agent'\."
240+
):
236241
await instructions_utils.inject_session_state(
237242
instruction_template, invocation_context
238243
)
@@ -343,6 +348,22 @@ async def test_inject_session_state_jinja2_artifact():
343348
assert populated_instruction == "Content: artifact data"
344349

345350

351+
@pytest.mark.asyncio
352+
async def test_inject_session_state_jinja2_missing_artifact_raises_key_error():
353+
instruction_template = "Content: {{ artifact('missing_file') }}"
354+
mock_artifact_service = MockArtifactService({})
355+
invocation_context = await _create_test_readonly_context(
356+
artifact_service=mock_artifact_service
357+
)
358+
359+
with pytest.raises(
360+
KeyError, match=r"Artifact 'missing_file' not found in agent 'agent'\."
361+
):
362+
await instructions_utils.inject_session_state(
363+
instruction_template, invocation_context, use_jinja2=True
364+
)
365+
366+
346367
@pytest.mark.asyncio
347368
async def test_inject_session_state_jinja2_undefined_variable_raises():
348369
instruction_template = "Hello {{ missing_var }}!"

0 commit comments

Comments
 (0)