Skip to content
PALM SOUQ

Agent loop taxonomy

Twelve agent loops and how to stop them

Every recursion trap we see in production, with its signature, the heuristic that catches it, a policy you can paste, and what it costs left unchecked.

Four heuristics · loops killed in under 50 ms · HTTP 429 with X-PalmSouq-Reason
gateway.palmsouqltd.com · breaker logtenant: northwind-ai
step 12  tool=run_tests  args#=9f3a  result=FAIL ModuleNotFoundErrorstep 13  tool=run_tests  args#=9f3a  result=FAIL ModuleNotFoundErrorstep 14  tool=run_tests  args#=9f3a  result=FAIL ModuleNotFoundErrorbreaker  tool_thrashing  identical_calls=3 window=8HTTP 429  X-PalmSouq-Reason: circuit_breaker_triggered

Unchecked

$1,200

Stopped at call 15

$7.20

Cyclical tool thrashing
Same tool called with identical or oscillating arguments inside a sliding window of steps.
3 identical calls in 8 steps
Token velocity surge
A tenant's token throughput exceeds a ceiling over a rolling window.
> 50k tokens/s over 10 s
Semantic echo loop
Responses or plans repeat near-identical content across steps, computed in memory without storing payloads.
3 near-identical outputs
Max step / recursion depth
Hard ceiling on steps per session, retries per task, or sub-agent nesting depth.
Per-policy cap, e.g. 25 steps

The catalog

Filter by the heuristic that catches it

Across the 12 examples, unchecked loops cost $2,295 at list prices. With breakers at the caps shown, the same incidents cost $39.

Tool thrashingStep and depth caps

Cyclical tool thrashing

The agent calls the same tool with the same arguments, gets the same result, and calls it again.

Signature
Identical tool name and argument hash repeating every one or two steps. Typical after a tool returns an error string the model does not understand, such as a test runner failing on a missing dependency.
Usual trigger
A tool result that never changes, combined with a prompt that tells the agent to keep trying until the task succeeds.
How PALM SOUQ detects it
PALM SOUQ hashes each tool call's name and arguments in memory and counts repeats inside a sliding window of steps. Three identical calls within eight steps trips the breaker by default.
breaker trace
step 12  tool=run_tests  args#=9f3a  result=FAIL ModuleNotFoundErrorstep 13  tool=run_tests  args#=9f3a  result=FAIL ModuleNotFoundErrorstep 14  tool=run_tests  args#=9f3a  result=FAIL ModuleNotFoundErrorbreaker  tool_thrashing  identical_calls=3 window=8HTTP 429  X-PalmSouq-Reason: circuit_breaker_triggered
palmsouq.policy.yaml
breakers:
  tool_thrashing:
    identical_calls: 3
    window_steps: 8
    action: block

Cost math at Claude Sonnet 4.5 list prices

150,000 input + 2,000 output tokens = $0.480 per call

Unchecked, 2,500 calls$1,200
With breaker, stopped after 15$7.20

The worked coding-loop example: 2,500 calls over 45 minutes with no cache hits.

Coverage

Which heuristic stops which trap

Primary detector in the first marked column; secondary detectors act as backstops when the primary is tuned loosely.

TrapTool thrashingToken velocitySemantic echoStep and depth capsUncheckedWith breaker
Cyclical tool thrashingPrimary··$1,200$7.20
Oscillating argumentsPrimary··$16$0.21
Retry storms on 429 and 5xx·Primary·$272$2.72
Self-reflection spirals··Primary$33$0.66
Sub-agent fan-out·Primary·$103$1.72
Planner and executor ping-pong··Primary$25$0.78
Browser navigation loopsPrimary··$72$0.72
Git merge-conflict loopsPrimary··$46$0.51
Schema-validation retry loops··Primary$195$23.40
Context-window overflow re-summarize loops··Primary$76$0.96
Semantic echo and degenerate output·Primary·$22$0.06
Token velocity surges·Primary··$235$0.14

How to

How to stop a runaway AI agent loop

Five steps to detect and stop recursive agent loops in production before they become a provider invoice, using an in-path gateway with circuit breakers.

agent.py
from openai import OpenAI, RateLimitError

client = OpenAI(
    base_url="https://gateway.palmsouqltd.com/v1",
    default_headers={
        "X-PalmSouq-Tenant": tenant.id,
        "X-PalmSouq-Feature": "research-agent",
    },
)

try:
    step = client.responses.create(model="gpt-5", input=messages)
except RateLimitError as e:
    reason = e.response.headers.get("X-PalmSouq-Reason")
    if reason == "circuit_breaker_triggered":
        session.halt(reason)  # do not retry
  1. 1

    Route agent traffic through the gateway

    Point your OpenAI, Anthropic or Gemini client at gateway.palmsouqltd.com and send X-PalmSouq-Tenant and X-PalmSouq-Feature headers so every call is attributed to a customer and a feature.

  2. 2

    Turn on the four heuristics

    Enable cyclical tool thrashing, token velocity (default 50k tokens per second over 10 seconds), semantic echo and a max step or depth cap in the tenant's policy.

  3. 3

    Choose the action when a breaker trips

    Block with HTTP 429 and X-PalmSouq-Reason: circuit_breaker_triggered, fall back to a cheaper model, or escalate the session to a human review queue.

  4. 4

    Handle the 429 in your agent

    Treat the breaker response as terminal for that session: stop retrying, save state, and show the user a clear message or hand off to support.

  5. 5

    Tune caps from real traffic

    Review steps per session and spend per tenant after the first week and set caps a margin above the 99th percentile, so legitimate long tasks finish and loops do not.

Loop detection questions

How does PALM SOUQ detect loops without storing prompts?

Under Zero Payload Retention, prompts and completions stream through memory and are never written to disk. The heuristics work on in-memory fingerprints: hashes of tool names and arguments, locality-sensitive fingerprints of outputs, token counts and timing. Only the fingerprints and counters needed for the sliding window are kept, and only for the length of that window.

Will a breaker kill legitimate long-running agents?

Caps are per policy, so a coding agent on an enterprise tier can allow far more steps than a free-tier chat feature. Most teams run in alert-only mode for the first week, look at steps per session and spend per tenant, then set caps a margin above the 99th percentile.

What does my agent receive when a breaker trips?

An HTTP 429 with X-PalmSouq-Reason: circuit_breaker_triggered, returned in under 50 ms before the request reaches the provider. Agent frameworks already handle 429 as a rate limit, so the loop stops; your code can read the reason header to show a specific message or hand the session to a human.

Which heuristic catches the most incidents?

Tool thrashing and step caps catch most tool-using agent loops, because the same call repeats. Token velocity catches bursts that do not repeat exactly, such as fan-out and retry storms. Semantic echo covers loops where the words repeat but the calls do not, such as reflection spirals.

Do I still need provider-side spend limits?

Keep them as a last line of defence. Provider limits apply to your whole organization and usually act after the money is spent; PALM SOUQ acts per tenant, in the request path, before the provider bills the call.

Put a circuit breaker in front of every agent.

Turn on all four heuristics in one policy file. Breakers trip in the request path, before the provider bills the next call.

14-day free production trial · 100k requests included · no card required