You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
doom_loop never fires when repeated calls span steps: check only reads the current assistant message #51965
Each step is its own assistant message. When the model makes one tool call per step, that message holds [step-start, tool] at the time of the check, so recentParts.length is 2 and the check returns early. The guard cannot fire however many times a call repeats across steps. It only catches three identical calls inside a single response.
This was reported in #25254, which the stale bot closed with a request to open a new issue if it was still relevant. It is still present in v1.18.33 and on dev. PR #32089 changes the check to count matching tool parts across the session and has been open since June. #45442 looks like the same cause, seen from a subagent.
What we saw. v1.18.33, local model through @ai-sdk/openai-compatible. In an 83-step session, every assistant message had exactly three parts: step-start, one tool, step-finish. From step 13 on, the model alternated between two bash calls about 70 times over roughly 10 minutes, until it was cancelled. Two user messages sent during the loop, one pointing it out and one redirecting the task, did not change the next call. The log has no doom_loop evaluation for the session. An earlier session on v1.18.32 alternated between two commands for 183 calls in the same way.
The alternation is also the pattern in #47759, but the scope problem hides it first: even a single command repeated once per step is never seen.
Plugins
None
OpenCode version
1.18.33 (also seen on 1.18.32)
Steps to reproduce
Leave doom_loop at its default (ask).
Use a model that makes one tool call per response, e.g. a local model through @ai-sdk/openai-compatible.
Get it to issue the same bash call with identical input in three or more consecutive steps.
No doom_loop prompt appears. In the session DB, each step's assistant message has parts [step-start, tool, step-finish].
Doom-loop detection misses periodic tool-call cycles #47759 covers periodic cycles within a single step. Our loop was also period-2, and I added our details there. Detecting periods alone would not have caught our case: with one tool call per step, the current check never sees more than one call.
This issue covers only the scope of the check, and that gap blocks the other two. Once the check spans the calls since the last user message, it catches repeats like #45442's, and period detection for #47759 can build on it.
Just encountered this as well with same root cause. I believe the MessageV2.filterCompactedEffect(ctx.sessionID) change from #32089 would fix the immediate issue.
I checked V2 dev 8c173a8f3805d8e749d44311054b07e37a1b2ce1. I couldn't find a cross-step repeat check in the runner; doom_loop still appears in the V1 config compatibility code. The runner has an optional steps limit, but that doesn't distinguish repetition from useful work.
I ran a scripted reproduction on 22e771f5893e255ea78fc10557ed3fb98d3a1072; the relevant runner files are unchanged in the newer commit. Eight identical failed tool calls across eight steps all execute, followed by a ninth model request to stop. There are no provider retry events. With steps=5, only four tool bodies execute. This proves the harness behavior, not how often real models get stuck.
Would a V2 follow-up here make sense, starting with consecutive identical calls across steps? I'd keep the history bounded and cover user input, intervening work and successful polling so legitimate retries aren't blocked. Changed-input failure feedback and periodic cycles could stay out of that first change.
Minimal reproduction in the existing runner test file
Add this scenario inside the existing SessionRunnerLLM describe block in packages/core/test/session-runner.test.ts:
scenario("tool failure feedback repro: eight failed steps",function*(s){constregistry=yield*Tool.Serviceconstcalls: string[]=[]yield*transformTools(registry,{lookup: {name: "lookup",description: "Look up a missing fixture resource",input: Schema.Struct({resource: Schema.String}),output: Schema.Struct({}),execute: ({ resource })=>{calls.push(resource)returnnewTool.Error({message: "Resource not found"})},},},{codemode: false})yield*s.admit("Look up the missing resource")yield*s.llm.push(
...Array.from({length: 8},(_,index)=>TestLLM.tool(`lookup-${index}`,"lookup",{resource: "missing"}),),TestLLM.stop(),)yield*s.resumeexpect(calls).toEqual(Array(8).fill("missing"))expect(s.requests).toHaveLength(9)constevents=yield*recordedEventTypes(sessionID)expect(events.filter((type)=>type==="session.tool.failed.2")).toHaveLength(8)expect(events).not.toContain("session.retry.scheduled.1")})
From packages/core, using Bun 1.4.2:
bun run script/test.ts test/session-runner.test.ts --test-name-pattern 'tool failure feedback repro'
The minimal reproduction passes. The broader local baseline has six cases, all passing across ten repetitions; the full runner file and Core typecheck also pass. No runtime behavior has been changed.
Thanks for the detailed report. This is against the V1 session processor, and the doom_loop check was removed in V2. Since V1 won't get a fix for this, we're closing it. Please update to the latest OpenCode release, and if you still see repeated tool-call loops there, open a new issue with your V2 version and repro steps.
Activity
github-actions commentedon Sep 29, 2026
This issue may overlap with existing reports of
doom_loopdetection gaps. Please check:processor.ts; it was closed for inactivity, and its comments include reports that the bug is still present.calebtt commentedon Sep 29, 2026
Thanks. I checked all three before filing, and they're linked in the description.
processor.tsis unchanged in v1.18.33 and ondev. PR fix(processor): detect doom loops across messages instead of within current message #32089 fixes it and is still open.This issue covers only the scope of the check, and that gap blocks the other two. Once the check spans the calls since the last user message, it catches repeats like #45442's, and period detection for #47759 can build on it.
gauntface commentedon Oct 7, 2026
Just encountered this as well with same root cause. I believe the
MessageV2.filterCompactedEffect(ctx.sessionID)change from #32089 would fix the immediate issue.lvjing2 commentedon Oct 17, 2026
I checked V2 dev
8c173a8f3805d8e749d44311054b07e37a1b2ce1. I couldn't find a cross-step repeat check in the runner;doom_loopstill appears in the V1 config compatibility code. The runner has an optionalstepslimit, but that doesn't distinguish repetition from useful work.I ran a scripted reproduction on
22e771f5893e255ea78fc10557ed3fb98d3a1072; the relevant runner files are unchanged in the newer commit. Eight identical failed tool calls across eight steps all execute, followed by a ninth model request to stop. There are no provider retry events. Withsteps=5, only four tool bodies execute. This proves the harness behavior, not how often real models get stuck.Would a V2 follow-up here make sense, starting with consecutive identical calls across steps? I'd keep the history bounded and cover user input, intervening work and successful polling so legitimate retries aren't blocked. Changed-input failure feedback and periodic cycles could stay out of that first change.
Minimal reproduction in the existing runner test file
Add this scenario inside the existing
SessionRunnerLLMdescribe block inpackages/core/test/session-runner.test.ts:From
packages/core, using Bun 1.4.2:bun run script/test.ts test/session-runner.test.ts --test-name-pattern 'tool failure feedback repro'The minimal reproduction passes. The broader local baseline has six cases, all passing across ten repetitions; the full runner file and Core typecheck also pass. No runtime behavior has been changed.
opencode-agent commentedon Oct 19, 2026
Thanks for the detailed report. This is against the V1 session processor, and the
doom_loopcheck was removed in V2. Since V1 won't get a fix for this, we're closing it. Please update to the latest OpenCode release, and if you still see repeated tool-call loops there, open a new issue with your V2 version and repro steps.