Nothing open on GitHub. Fix merged in #28592 on 19 May 2026. Check it covers your case.
The short answer
0open issues
0open PRs
3items grouped
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #28592. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #28592. That is separate from the fix event. - Still open
- Nothing open on GitHub.
These items address adding recovery hints to tool loop warnings in the agent and related gateway tool-use hardening, including skill preload and loop-guard improvements. The work centers on improving guidance when tool loops are detected.
Open issues0
Open PRs0
Fixes on record
-
#28592 fix(agent): add recovery hints to tool loop warnings (#28256)
files:
agent/tool_guardrails.py,tests/agent/test_tool_guardrails.py,tests/run_agent/test_tool_call_guardrail_runtime.pyShow the diff
-
agent/tool_guardrails.py@@ -336,10 +336,7 @@ def after_call( return ToolGuardrailDecision( action="warn", code="same_tool_failure_warning", - message=( - f"{tool_name} has failed {same_count} times this turn. " - "This looks like a loop; change approach before retrying." - ), + message=_tool_failure_recovery_hint(tool_name, same_count), tool_name=tool_name, count=same_count, signature=signature, @@ -406,6 +403,26 @@ def append_toolguard_guidance(result: str, decision: ToolGuardrailDecision) -> s return (result or "") + suffix +def _tool_failure_recovery_hint(tool_name: str, count: int) -> str: + """Action-oriented guidance for recovering from repeated tool failures.""" + common = ( + f"{tool_name} has failed {count} times this turn. This looks like a loop. " + "Do not switch to text-only replies; keep using tools, but diagnose before retrying. " + "First inspect the latest error/output and verify your assumptions. " + ) + if tool_name == "terminal": + return common + ( + "For terminal failures, run a small diagnostic such as `pwd && ls -la` " + "in the same tool, then try an absolute path, a simpler command, a different " + "working directory, or a different tool such as read_file/write_file/patch." + ) + return common + ( + "Try different arguments, a narrower query/path, an absolute path when relevant, " + "or a different tool that can make progress. If the blocker is external, report " + "the blocker after one diagnostic attempt instead of repeating the same failing path." + ) + + def _coerce_args(args: Mapping[str, Any] | None) -> Mapping[str, Any]: return args if isinstance(args, Mapping) else {} -
tests/agent/test_tool_guardrails.py@@ -160,6 +160,10 @@ def test_same_tool_varying_args_warns_by_default_without_halting(): assert first.action == "allow" assert [second.action, third.action, fourth.action] == ["warn", "warn", "warn"] assert {second.code, third.code, fourth.code} == {"same_tool_failure_warning"} + assert "Do not switch to text-only replies" in second.message + assert "keep using tools" in second.message + assert "diagnose before retrying" in second.message + assert "different tool" in second.message assert controller.halt_decision is None -
tests/run_agent/test_tool_call_guardrail_runtime.py@@ -153,6 +153,37 @@ def test_sequential_after_call_appends_guidance_to_tool_result_without_extra_mes assert "repeated_exact_failure_warning" in messages[0]["content"] +def test_same_tool_failure_warning_tells_model_to_recover_with_tools(): + agent = _make_agent("terminal") + guardrails = getattr(agent, "_tool_guardrails") + guardrails.after_call( + "terminal", + {"command": "bad-1"}, + json.dumps({"exit_code": 1}), + failed=True, + ) + guardrails.after_call( + "terminal", + {"command": "bad-2"}, + json.dumps({"exit_code": 1}), + failed=True, + ) + tc = _mock_tool_call("terminal", json.dumps({"command": "bad-3"}), "c-recover") + msg = SimpleNamespace(content="", tool_calls=[tc]) + messages = [] + + with patch("run_agent.handle_function_call", return_value=json.dumps({"exit_code": 1})): + agent._execute_tool_calls_sequential(msg, messages, "task-1") + + content = messages[0]["content"] + assert "same_tool_failure_warning" in content + assert "Do not switch to text-only replies" in content + assert "keep using tools" in content + assert "pwd && ls -la" in content + assert "absolute path" in content + assert "different tool" in content + + def test_config_enabled_hard_stop_concurrent_path_does_not_submit_blocked_calls_and_preserves_result_order(): agent = _make_agent("web_search", config=_hard_stop_config()) blocked_args = {"query": "blocked"}
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#28256 fix(agent): add recovery hints to tool loop warnings · merged via another change →
d759a67,#28592
Closed issues · 1
- Closed#28204Gateway tool-use hardening: skill preload, loop-guard recovery, path resolution docs
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #28592
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 19 May 2026