Fix: escalate repeated load_skill_resource misses to RESOURCE_NOT_FOUND_FATAL - #831
Open
AmaadMartin wants to merge 4 commits into
Open
Fix: escalate repeated load_skill_resource misses to RESOURCE_NOT_FOUND_FATAL#831AmaadMartin wants to merge 4 commits into
AmaadMartin wants to merge 4 commits into
Conversation
added 4 commits
August 8, 2026 17:06
…ND_FATAL A model that guesses a skill resource path got the same soft RESOURCE_NOT_FOUND on every miss, so it could retry until the invocation ended. Count misses per invocation in temp: state and return RESOURCE_NOT_FOUND_FATAL from the second miss onward, matching adk-python. The six existing error-code literals move into an exported LoadSkillResourceErrorCode enum; the emitted strings are unchanged.
Adds four cases: escalation on the second miss, escalation on a different path, a running count across the references/, assets/ and scripts/ prefixes, and a per-invocation reset that also pins the temp: counter key shape.
Runs the guard through InMemoryRunner, LlmAgent and InMemorySessionService with a scripted model. Proves the escalation and the per-invocation reset over real invocation ids, and that the counter key never reaches the stored session state.
The stripper that keeps this counter out of durable storage matches on State.TEMP_PREFIX, so the key must be built from the same constant rather than a literal that can drift.
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
N/A
Problem:
load_skill_resourcereturns the same softRESOURCE_NOT_FOUNDon every miss. A model that guesses a resource path gets the same "try again" signal each time, so it can retry until the invocation ends.adk-pythonalready escalates from the second miss (skill_toolset.py,LoadSkillResourceTool.run_async).Solution: The tool now counts misses per invocation and returns
RESOURCE_NOT_FOUND_FATALfrom the second miss onward, with a message that tells the model to stop. The counter lives intoolContext.stateundertemp:_adk_skill_resource_not_found_count_<invocationId>, the same key shapeadk-pythonuses: thetemp:prefix keeps it out of persisted session state, and the invocation id resets the count each invocation. The six existing error-code literals move into an exportedLoadSkillResourceErrorCodeenum; the emitted strings do not change.Behaviour change: a caller that saw
RESOURCE_NOT_FOUNDon the 2nd+ miss in one invocation now seesRESOURCE_NOT_FOUND_FATAL. That is the fix, and it matchesadk-python.Parity vs. local convention: parity wins on the wire — error-code strings, the counter key, and the fatal message text (including the em dash) match
adk-pythonbyte for byte. Local convention wins in-process — an enum instead of bare literals, and the existing snake_caseerror_coderesponse key stays as it is in this file.Collision check:
gh pr list --repo AmaadMartin/adk-js --state open --limit 1000returned 720 open PRs. None implements this guard. #761 edits the same two files but in different hunks (the binary-resource branch, not the miss branch) and #729 ports the siblingrun_skill_scriptguard in a different file, so this branches frommain.Testing Plan
Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.
Unit Tests:
[x] I have added or updated unit tests for my change.
[x] All unit tests pass locally.
Commands run on the pushed commit:
npm run ts:checkreports 287 errors on this branch and 287 onmain, none in the files this PR touches. That breakage is pre-existing and unrelated.Coverage of the new code is 100% of lines and branches, measured with
--coverage.include='**/tools/skill/load_skill_resource_tool.ts': every statement and branch in the new block (source lines 141-162) is hit.Proof the tests can fail. Each mutation was applied to the source alone, then reverted:
failCount > 1->failCount > 2expected [ 'RESOURCE_NOT_FOUND', …(1) ] to deeply equal [ 'RESOURCE_NOT_FOUND', …(1) ]${toolContext.invocationId}from the keyresets the counter for a new invocation idfailsState.TEMP_PREFIXfrom the key prefixresets the counter for a new invocation idfails, and the agent-run test fails withexpected [ …(2) ] to deeply equal []— the counter key reaches the stored session stateScope note: the plan listed a fifth unit test for the first-miss soft error. The existing test
returns error if resource not foundalready pins that exact response with full-object equality, so I did not add a duplicate.Manual End-to-End (E2E) Tests:
core/test/tools/skills/load_skill_resource_guard_run_test.tsruns the guard through a realInMemoryRunner,LlmAgent,SkillToolsetandInMemorySessionService; only the model is scripted. It asserts the escalation over real invocation ids and that no_adk_skill_resource_not_found_count_*key survives in the stored session.To reproduce by hand: give an agent a
SkillToolset, then in one invocation ask for two resource paths that do not exist. The first tool response carriesRESOURCE_NOT_FOUNDand the second carriesRESOURCE_NOT_FOUND_FATAL. Start a second invocation on the same session; its first miss isRESOURCE_NOT_FOUNDagain.Checklist
[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.