Claude Agent Infinite Loop Detection — Fix (2026) | AI Error Hub
Anthropic Claude Agents / Tool Loops Severity: Critical Cost issue

Claude Agent Stuck in Infinite Tool Call Loop

Your agent keeps calling the same tool with the same or nearly-same inputs, burning tokens without progress. Common when tool returns errors Claude misinterprets, when Claude hallucinates a tool, or when tool feedback is ambiguous. Prevent with iteration caps and duplicate-call detection.

Error surface: Agent behavior Category: Agents / Tool Loops Symptom: Repeated (name,input) tuples Last tested: Nov 15, 2026
Quick Fix (TL;DR)

Fingerprint every tool call as (name, hash(input)). If the same fingerprint appears 3+ times, inject a system message telling Claude the loop is detected. Also enforce a max iteration cap (10-20 turns typical), timeout the whole run, and log each fingerprint for post-mortem debugging.

The Symptoms You're Seeing

Loop patterns to detect
Turn 1: tool_use search_docs("payment refund")
Turn 2: tool_use search_docs("payment refund")   ← same call
Turn 3: tool_use search_docs("payment refund")   ← same call
Turn 4: tool_use search_docs("payment refund")   ← infinite

Or: input drift while going nowhere
Turn 1: tool_use calc(x + y)
Turn 2: tool_use calc(x + y + 0)
Turn 3: tool_use calc(x + y + 0.0)
Turn 4: tool_use calc((x + y))

Symptoms:
- Token spend spikes without task completion  
- Agent runs hit iteration cap  
- User complaints: "it just keeps searching"  
- Same tool_use_id patterns in logs  
- Cost anomaly alerts firing

What Actually Causes Loops

32%
Tool returns error Claude misreadsTool returns "not found"; Claude retries assuming transient failure.
24%
Ambiguous tool resultTool returns empty list; Claude retries with slight variation.
18%
Missing state feedbackTool succeeds but doesn't confirm; Claude retries thinking nothing happened.
14%
Hallucinated toolClaude imagines a tool that doesn't exist; user turn rejects; Claude re-tries.
8%
Circular dependency between toolsTool A needs data from B; B needs data from A.
4%
tool_choice: "any" with only one toolForces Claude to keep calling the same tool forever.

Fixes That Work (Tested Nov 2026)

1Fingerprint-Based Loop Detection

Python · detect and break loops
import hashlib, json from collections import Counter class LoopDetector: def __init__(self, threshold=3, window=10): self.threshold = threshold self.window = window self.fingerprints = [] def fingerprint(self, name, input_dict): payload = json.dumps({"n": name, "i": input_dict}, sort_keys=True) return hashlib.md5(payload.encode()).hexdigest()[:12] def record(self, name, input_dict): fp = self.fingerprint(name, input_dict) self.fingerprints.append(fp) # Keep sliding window self.fingerprints = self.fingerprints[-self.window:] return fp def is_looping(self): if len(self.fingerprints) < self.threshold: return False, None counter = Counter(self.fingerprints) most_common, count = counter.most_common(1)[0] return count >= self.threshold, most_common # In agent loop detector = LoopDetector(threshold=3) async def run_agent(messages, max_iterations=15): for iteration in range(max_iterations): response = await client.messages.create(...) for block in response.content: if block.type == "tool_use": fp = detector.record(block.name, block.input) looping, culprit = detector.is_looping() if looping: # Inject system nudge to break loop messages.append({ "role": "user", "content": [{ "type": "tool_result", "tool_use_id": block.id, "content": ( "System: You've called this tool with the same " "input 3 times. Try a different approach or " "give a final answer based on what you know." ), "is_error": True }] }) continue # ... normal flow

2Hard Iteration Cap + Timeout

Python · bounded agent runs
import asyncio from time import time async def bounded_agent_run( initial_message, max_iterations=20, max_wall_time_sec=120, max_total_tokens=100_000 ): messages = [{"role": "user", "content": initial_message}] start_time = time() total_tokens = 0 for iteration in range(max_iterations): if time() - start_time > max_wall_time_sec: return {"status": "timeout", "iterations": iteration} if total_tokens > max_total_tokens: return {"status": "token_cap", "tokens": total_tokens} response = await client.messages.create( model="claude-sonnet-4-5", max_tokens=4096, tools=my_tools, messages=messages ) total_tokens += response.usage.input_tokens + response.usage.output_tokens messages.append({"role": "assistant", "content": response.content}) if response.stop_reason == "end_turn": return {"status": "complete", "response": response} # ... execute tools, append results return {"status": "iteration_cap", "iterations": max_iterations}
Iteration cap sizing

Typical caps: 10 for simple lookup, 20 for research, 50 for complex multi-tool workflows. Above 50, question whether Claude is really progressing or just churning. Log final iteration count; adjust caps based on real workload data.

3Improve Tool Feedback Quality

Python · unambiguous tool results
# BAD — ambiguous feedback def search_docs(query): results = db.search(query) if not results: return "" # Claude may retry thinking it failed return results # GOOD — explicit, actionable feedback def search_docs(query): results = db.search(query) if not results: return { "status": "no_results", "query": query, "message": ( f"No documents match '{query}'. " "Try a broader query, or answer from general knowledge." ), "suggested_alternatives": [ "try shorter query", "try different keywords", "proceed without documentation lookup" ] } return {"status": "ok", "count": len(results), "results": results} # Now Claude knows: retry won't help. Try alternative or give up.

Preventing Loops Going Forward

  • Fingerprint tool calls. Detect repeats in a sliding window.
  • Cap iterations, wall time, and token budget. Three independent bounds.
  • Design tool results to be actionable. Never return empty strings; explain what to try next.
  • Log every fingerprint for post-mortem. Identify loop-prone tools.
  • Add a "give up" tool. Let Claude call report_stuck(reason) gracefully.
  • Monitor cost per agent run. Alert on outliers > 3× baseline.
AR
Tested by Ahmed R.
Researcher · AI Error Hub
Last verified: Nov 15, 2026 · SDK: anthropic 0.42

Frequently Asked Questions

Yes, and give it explicit permission. Add a report_stuck or give_up_gracefully tool. Tell Claude in the system prompt: "If you've tried and can't make progress, call report_stuck with your reasoning." Better than silent looping.

Thinking often HELPS avoid loops by letting Claude reflect on prior attempts. But if broken feedback confuses reasoning, thinking amplifies the loop. Monitor loop rates with/without thinking enabled.

Anthropic won't stop your agent from looping — it's your responsibility. Anthropic charges for tokens generated regardless. You must implement client-side limits. Consider budget alerts in the console.

Cache reduces cost per turn but doesn't stop looping. It's independent of loop detection. Some teams see loops WORSE with cache because per-request cost is lower, so cost signals fire later.

Task-dependent. Simple lookup: $0.01-0.05. Research task: $0.10-1.00. Multi-hour analysis: $1-10. Set alerts at 3× your P95 baseline. Runs over the alert threshold get inspected for loops.