Your agent keeps calling the same tool forever — find the causes and the guards.
An agent runs against a search tool and never finishes. The trace repeats verbatim until the process is killed.
Constraints:
- The tool itself is healthy — it returns HTTP 200 on every call.
- Older turns are trimmed from the context to fit the window.
- Name the causes this trace is consistent with, then give the guards you would add.
step 41 thought: I need the release date. action: search{"q": "release date"}
step 41 observation: []
step 42 thought: I need the release date. action: search{"q": "release date"}
step 42 observation: []
step 43 thought: I need the release date. action: search{"q": "release date"}
step 43 observation: []
Find the causes and fix the loop.
The turn repeats because nothing changes between steps — the observation carries no new information, or the model cannot see it already tried that call. Guard it with a step budget, a repeat detector, past actions in context, and richer observations.
- ✗Adding a step budget only, without asking why the observation never changes
- ✗Assuming the model remembers its own past calls after they were trimmed away
- ✗Reading a repeated call as a flaky tool and hiding it behind client retries
- →What should the runtime do when the repeat detector fires — abort or steer?
- →Why can an empty tool result be worse than an explicit error message?
The trace is consistent with two causes at once. First, the observation is an empty list, and emptiness tells the model nothing new, so it rephrases the same thought. Second, older turns were trimmed away, so the model cannot see that it already made this call.
guard 1 step budget → hard stop at N steps, return a partial answer
guard 2 repeat detector → hash (tool, args) over the last k calls; identical → intervene
guard 3 action memory → keep a compact list of past (tool, args, outcome) in context
guard 4 rich observation → "0 results for q=... ; try a different query" instead of []
Order matters: the step budget only bounds the damage, while guards 3 and 4 remove the cause. When the detector fires it is usually better to steer than to abort — return an observation stating that this call was already made with the same result.