Docs / Observability / Loops
loops
Repetitive agent behavior: too many steps, identical LLM calls fired in a row, or near-identical calls clustered together. The loops detector covers three sub-patterns that share a mental model ("the agent is stuck instead of making progress") but answer different specific questions. All three sub-signatures live under one failure class so they cluster as one chip in the dashboard.
What this detector catches
Four sub-signatures:
- step_count_<bucket> — total telemetry events in a single execution exceeded a healthy-agent threshold. Bucketed:
step_count_10+(10-49 events),50+(50-99),100+(100-499),500+(500-4,999),5000+(5,000+). The right bucket usually tells you which scale of remediation you need: the 10-49 range is a retry loop, the 5000+ range is a runaway. - identical_call_<hash> — three or more
llm_callevents in one execution with the exact same (model, user_message) pair. Mesedi hashes(model + user_message)per call and flags executions where the same hash recurs 3+ times. The most reliable signal because exact-match is unambiguous. - similar_call_<hash> — a cluster of 3+
llm_callevents whose user_message text falls within a cosine-distance threshold (default 0.20). Catches the case where the prompt drifts slightly each iteration (timestamps, counters, "try again"-style reformulations) but the underlying request is the same. - time_budget_<bucket> — the execution's wall-clock duration exceeded the configured time budget. Bucketed:
time_budget_1s+,_10s+,_60s+,_10m+,_1h+. Catches the stuck-but-not-step-bound case where a single LLM call or tool call hangs.
Example failures it groups
- Retry logic without backoff or jitter — the agent retries a failed call by re-running the exact same prompt (identical_call).
- A reasoning loop where the model keeps reaching the same conclusion and the agent keeps re-asking with only a counter increment (similar_call).
- A long-running planning agent that emits hundreds of tool_calls + checkpoints (step_count_500+).
- A "try-then-validate-then-retry" loop where the validator keeps rejecting and the agent keeps re-prompting (similar_call OR identical_call depending on whether the prompt changes).
What to do when it fires
The failure-group detail page shows a Playbook specific to the sub-signature that fired (one of 4 markdown files: identical_call.md / similar_call.md / step_count.md / time_budget.md). Common remediation patterns across all 4: identical_call almost always means a missing backoff or a deterministic-failure retry path — wrap the LLM call with retry-with-backoff and a max-attempt cap; similar_callmeans the prompt is drifting on a dimension that doesn't advance the agent's progress (often a counter or timestamp embedded in the system prompt) — strip the drifting dimension or cap the cluster at a max-iteration count; step_count means the agent has no termination condition — add a hard step budget and surface budget-exceeded as a structured error so the agent can escalate to a human instead of continuing; time_budget is the wall-clock equivalent — either a single call is hanging (add per-call timeouts and retries with a smaller window) or the cumulative work exceeds what the project budget allows (raise time_budget_ms in Settings if the workload is intentionally long, or split into smaller executions).
When NOT to investigate
- Long-running research agents that legitimately need 100+ steps. Raise
step_count_threshold(andtime_budget_msif the project is also tripping the wall-clock budget) — see Tunable thresholds below. - Agents with intentional re-asks where each iteration adds new context (RAG with progressive retrieval, multi-step planners). These produce similar_call clusters that are correct behavior, not failure — lower
similar_call_distance_threshold(a tighter cluster needs more lexical agreement to fire) or raisesimilar_call_min_cluster_sizeto reduce false positives. - Bulk-processing agents where the same prompt template applies to many items in a row (each call is genuinely independent work, just structurally similar). identical_call will fire here; consider whether each item should run in its own execution rather than batching N into one.
Tunable thresholds + allowlist
All four sub-signatures have per-project tunables (none tier-capped except time_budget_ms). Tune via the Detector thresholds primitive (the first four) or the dedicated GET/PUT /me/time-budget-config endpoint (the last one):
step_count_threshold(default 10; bounds [2, 10000]). Total events per execution above which loops/step_count fires.identical_call_min_repeats(default 3; bounds [2, 100]). Distinct repeats of the same(model, user_message)hash before identical_call fires.similar_call_distance_threshold(default 0.20; bounds [0.05, 0.50]). Cosine distance below which two user_messages count as similar.similar_call_min_cluster_size(default 3; bounds [2, 100]). Minimum cluster size before similar_call fires.time_budget_ms(default 60_000; tier-capped viaGET /me/time-budget-config— Hobby / Team / Enterprise have distinct max values). Wall- clock duration above which time_budget fires.
The loops detector does NOT support the per-project Allowlist primitive today (only crashes, tool_failures, and validator_failures do). To silence loops in specific executions, use the dashboard's severity routing to downgrade the failure class to info-tier, or tune the thresholds above.
Related docs
- token_waste — the parallel signal on the same underlying pattern (identical/similar LLM calls). token_waste groups by prefix-hash and surfaces under its own failure class; loops surfaces the same data under the "repetitive behavior" framing. Both fire when applicable — by design, they answer different questions about the same calls.
- semantic_loop — the "checkpoint state revisited" detector, a sibling pattern that operates on customer-emitted
checkpointevents rather than on LLM calls.