{"record":{"id":"a6bb395a99fd1890","repo":"zeroclaw-labs/zeroclaw","slug":"agent-loop-aborted-by-loop-detector-msg","errorCode":null,"errorMessage":"Agent loop aborted by loop detector: {msg}","messagePattern":"Agent loop aborted by loop detector: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/agent/turn/results_collect.rs","lineNumber":120,"sourceCode":"                        format!(\"[Loop Detection — BLOCKED] {msg}\"),\n                    );\n                }\n                crate::agent::loop_detector::LoopDetectionResult::Break(msg) => {\n                    ::zeroclaw_log::record!(\n                        WARN,\n                        ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                            .with_category(::zeroclaw_log::EventCategory::Tool)\n                            .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                            .with_attrs(::serde_json::json!({\n                                \"model\": model,\n                                \"iteration\": iteration + 1,\n                                \"tool\": tool_name,\n                                \"message\": msg,\n                                \"trace_id\": turn_id,\n                            })),\n                        \"loop_detector_circuit_breaker\"\n                    );\n                    anyhow::bail!(\"Agent loop aborted by loop detector: {msg}\");\n                }\n            }\n        }\n        let canonical_output =\n            canonicalize_tool_result_media_markers_for(&tool_name, &outcome.output);\n        let mut result_output = truncate_tool_result(&canonical_output, max_tool_result_chars);\n        // Append HMAC receipt to tool result when receipts are enabled\n        if let Some(ref receipt) = outcome.receipt {\n            ::zeroclaw_log::record!(\n                DEBUG,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)\n                    .with_category(::zeroclaw_log::EventCategory::Tool)\n                    .with_attrs(::serde_json::json!({\"tool\": tool_name, \"receipt\": receipt})),\n                \"Tool receipt generated\"\n            );\n            result_output = format!(\"{result_output}\\n\\n[receipt: {receipt}]\");\n            if let Some(store) = collected_receipts\n                && let Ok(mut v) = store.lock()","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/agent/turn/results_collect.rs#L102-L138","documentation":"collect_tool_results feeds every successful tool outcome into loop_detector.record(tool, args, output). When detection escalates past Warning (advice appended to history) and Block (output replaced with feedback) to Break — the circuit breaker — the whole turn aborts with the detector's message. This is the last-resort protection against runaway repetitive tool calling.","triggerScenarios":"The model repeatedly issues effectively identical tool calls (same name and arguments), ignores injected Warning and Block feedback messages, and keeps going; only successful outcomes are recorded, so a pattern needs succeeding calls to trip the breaker.","commonSituations":"Model fixated on a tool whose output it misreads (unparseable format, truncated result); prompts with no termination criterion; under-powered models looping on one step; tools whose errors look like success.","solutions":["Read the detector msg plus the earlier Warning/Block log entries to identify the repeating tool and arguments","Fix the tool output or prompt so the model can act on the result (clearer text, structured output, explicit completion markers)","Switch to a stronger model or add explicit stop conditions to the system prompt","Only after fixing the repetition, consider tuning loop-detector thresholds"],"exampleFix":"// before: system prompt gives no completion criteria\n\"Keep working on the task.\"\n\n// after: bound the loop explicitly\n\"Attempt each step once. If a tool result repeats or you already have the answer, summarize findings and stop.\"","handlingStrategy":"validation","validationCode":"fn is_repetitive(calls: &[ToolCall], window: usize) -> bool {\n    let key = |c: &ToolCall| (c.name.clone(), c.arguments.to_string());\n    if calls.len() < window { return false; }\n    let last = key(&calls[calls.len() - 1]);\n    calls[calls.len() - window..].iter().filter(|c| key(c) == last).count() >= window\n}","typeGuard":null,"tryCatchPattern":"match agent.run_turn(req).await {\n    Err(ref e) if e.to_string().starts_with(\"Agent loop aborted by loop detector\") => {\n        // inspect which tool/args repeated from logs; fix prompt/tool output before retry\n    }\n    other => other,\n}","preventionTips":["Give tools unambiguous, structured outputs the model can act on","Add explicit stop conditions to system prompts for open-ended tasks","Vary arguments when retrying — identical name+args is what the detector keys on","Watch for Warning/Block feedback messages in history; they precede the breaker"],"tags":["loop-detector","circuit-breaker","repetitive-tool-calls","agent-loop"],"backgroundTag":"infinite-loop-detected","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}