Skip to content

Doom-loop detection misses periodic tool-call cycles #47759

Description

@yixiao5428

Description

The doom_loop guard detects three adjacent identical tool calls, but misses alternating calls such as A, B, A, B, A, B. Repeating the same multi-call block never satisfies the current last-three-parts comparison.

I reproduced this with a deterministic normalized stream driving the real SessionProcessor, without changing production code. The period-2 regression fails because processing returns continue without asking permission. A period-1 control (A, A, A, with distinct call IDs) passes.

Plugins

None in the reproduction fixture.

OpenCode version

Upstream dev at 57ef3828431790c53f8f333c7ffbfe88770a1812.

Steps to reproduce

  1. Configure doom_loop: "ask" and create a fresh assistant processor.

  2. Supply a normalized stream with step-start, followed by these calls in order. Give every call a unique ID and follow each with a successful tool-result:

    lookup({query: "a"})  call-a1
    search({query: "b"})  call-b1
    lookup({query: "a"})  call-a2
    search({query: "b"})  call-b2
    lookup({query: "a"})  call-a3
    search({query: "b"})  call-b3
    
  3. End with step-finish(tool-calls) and finish(tool-calls).

  4. Observe six completed tool parts, no permission.asked event, and processor result continue.

Expected: three complete repetitions of this two-call block should reach the existing doom_loop permission gate, just as three repetitions of a single call do.

This is distinct from #46272's consecutive-identical-call stop threshold and #32089's cross-message history scan. The sequence above occurs entirely within one active processor. It does not require changing permission actions or adding an unconditional stop.

Operating System

Linux. Reproduced in the processor Effect test suite, without an external provider.

Activity

chinatsu1124 commented on Sep 22, 2026

@chinatsu1124

Hit this in the wild on 1.18.32 (WSL2, model mimo-v2.6-pro via opencode-go).

Two occurrences in one session, each inside a single step:

  • 233 tool calls, period 2 (two bash commands alternating, 109x each), step ended with finish: "unknown" after ~28.7k output tokens
  • 236 tool calls, period 4 (bash A, bash B, websearch C, websearch D repeated ~59x), stream never finished (no step-finish, last call pending with {} input)

No three adjacent parts were ever identical, so doom_loop never fired. All calls ran to completion (~4–5 min each). The model was clearly degenerating, but the guard should have caught a block repeated 50+ times.

A per-step cap on total tool calls, or counting identical (tool, input) pairs within the step, would have stopped both.

calebtt commented on Sep 29, 2026

@calebtt

We ran into this too, at an inconvenient moment: partway through a task to repair a missing Wi-Fi driver.

v1.18.33, local Qwen3.8 27B through KoboldCpp's OpenAI-compatible endpoint. The model alternated between two bash calls, ls <device>/ | grep -E 'driver|remove|rescan' and ls -la <device>/ | grep -E 'driver|remove|rescan', about 70 times over roughly 10 minutes. It kept going after a user message pointing out the loop and had to be cancelled. An earlier session on 1.18.32 did the same with a period-2 nmcli/curl pair for 183 calls.

One difference from the in-step repro above: our model made one tool call per step, so each repetition was a separate assistant message. The current check only reads the current message's parts, so it never saw more than one call (filed as #51965; originally #25254). Period detection within a single message would not catch this case. It needs to run over the tool calls since the last user message.

The model-side trigger was our inference setup (thinking disabled, no repetition penalty), which we have since fixed, but the guard never had a chance to step in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions