Repository navigation
Closed as not planned
Closed as not planned
Copy link
Description
Bug: OpenCode Enters Infinite Loop After Tool Calls Complete
Description
OpenCode (opencode) enters an infinite loop and stops responding to user input after completing tool calls. The process stays alive but never exits or continues meaningfully.
Affected Versions
- Big Pickle (opencode/big-pickle) with OpenCode Zen provider
⚠️ PRIMARY ISSUE - v1.1.60+ (GitHub Copilot provider with claude-sonnet-4.6) - similar symptoms
- v1.3.0+ (with OpenAI-compatible providers) - similar symptoms
- Issue
opencode runhangs after completing tool calls — process never exits #17516 confirmed in v1.1.65 through v1.3.0
Symptoms
- Process never exits:
opencode runhangs indefinitely after tool calls complete - Silent infinite loop: Session processor keeps calling LLM with empty responses
- No CPU usage: Process alive at 0-2% CPU, doing nothing useful
- No error output: Logs show no errors, no timeouts
- Parent sessions block: When used as subagent, parent hangs forever waiting
Root Cause (from issue #17516)
OpenCode doesn't detect "done" state properly.
After the model finishes tool calls and produces final text response:
- Session processor should detect
finish_reason=stopor "no tool calls in text-only response" - Instead, it treats the response as requiring another iteration
- Calls LLM again with same context
- Creates infinite loop of empty LLM calls
Timeline from debug logs:
18:53:24 | step=0 | Session created, prompt resolved
18:53:27 | step=1 | Model calls Read/Write → both succeed
18:53:32 | step=2 | LLM stream → NO tool call, NO visible output
18:53:35 | step=3 | Same pattern — empty loop iteration
...
18:55:21 | step=42 | Still looping when killed at 120s
Pattern per step (steps 2-42):
session.prompt step=N
session.prompt status=started resolveTools
tool.registry status=started/completed (all 12 tools)
permission evaluate (task general, task explore)
session.prompt status=completed resolveTools
session.processor process
llm providerID=opencode/big-pickle modelID=zen stream
→ next step (infinite loop)
Related Issues Found
| Issue | Description | Status |
|---|---|---|
| #17516 | opencode run hangs after completing tool calls |
Open |
| #11153 | Session loop doesn't stop when model returns finish_reason=stop | Closed (fixed in #11152) |
| #19208 | opencode run adds empty stop-turn after tool-calls completion | Closed (dup of #17982) |
| #21250 | TUI/task runs stall after last completed tool; child tasks spawn empty assistant run | Open |
| #20096 | Tool and task execution can hang indefinitely with no timeout | Open |
| #13841 | Explore subagent hangs indefinitely with Anthropic Claude Opus 4.6 | Open |
| #11865 | Tasks/Subagents with Codex/OpenAI get stuck with no timeout/retry | Open |
Reproduction Steps
# Create a prompt that uses tools
cat > /tmp/test-prompt.md << 'EOF'
Read compress.py and replace the RLE implementation with zlib.
Edit the file directly. Do not run any commands.
EOF
# Run with debug logging - using Big Pickle + Zen provider
opencode run --print-logs --log-level DEBUG \
-m opencode/big-pickle \
< /tmp/test-prompt.md 2>debug.log
# Process hangs after tool calls complete
# debug.log shows session.prompt stepping infinitely with no tool calls
# LLM provider: opencode/big-pickle with Zen
Expected Behavior
After the model's final text response (post-tool-call), OpenCode should:
- Detect that the model has finished (finish_reason=stop or no tool calls)
- Exit cleanly with code 0
- NOT call LLM again
Actual Behavior
OpenCode:
- Treats final response as requiring another iteration
- Calls LLM again with same context
- Loops infinitely with empty LLM calls every ~2.5s
- Never exits, never produces output
- Must be killed manually (Ctrl+C or kill -9)
Workarounds
- Switch models: Use
gpt-5.3-codexinstead ofclaude-sonnet-4.6(works perfectly) - Switch providers: Use different provider that sends proper stop signals
- Manual kill + restart:
Ctrl+Cand rephrase prompt
Suggested Fixes
Fix 1: Detect finish_reason=stop (Primary)
In processor.ts, the process() function should return "stop" when finish_reason=stop:
// In processor.ts process() function
if (response.finish_reason === 'stop' && !response.tool_calls) {
return "stop"; // Currently returns "continue"
}
Fix 2: Break on Empty Responses
If LLM is called but produces NO tool calls and NO text, treat as done:
// After LLM stream completes
if (!response.content && (!response.tool_calls || response.tool_calls.length === 0)) {
logger.info('Empty response detected, stopping loop');
return "stop";
}
Fix 3: Add Loop Detection
Detect when same prompt step repeats without progress:
// Track consecutive empty responses
let consecutiveEmptyResponses = 0;
if (isEmptyResponse(response)) {
consecutiveEmptyResponses++;
if (consecutiveEmptyResponses > 3) {
logger.warn('Infinite loop detected, forcing stop');
return "stop";
}
}
Fix 4: Add Timeout Protection
Add configurable timeout for headless mode:
{
"experimental": {
"headless_timeout": 300000 // 5 minutes
}
}
Environment
- OpenCode version: big-pickle (opencode/big-pickle)
- Affected providers: OpenCode Zen (primary issue)
- Also affected: claude-sonnet-4.6 via github-copilot provider (similar symptoms)
- Works fine: gpt-5.3-codex, simple prompts without tool calls
- Process state: Alive, 0-2% CPU, consuming 500-900MB RSS
- Key difference: Zen provider with big-pickle model causes mid-process stopping/looping
Impact
- Breaks automation: CLI tools that spawn
opencode runas subprocess hang forever - Wastes API calls: Infinite loop consumes quota/credits
- Blocks parent sessions: Subagent hangs block parent indefinitely
- Poor UX: No error message, no indication of what's wrong
References
- Original issue:
opencode runhangs after completing tool calls — process never exits #17516 - Related fix attempt: fix(session): stop loop when model returns non-tool finish reason #11152
- Session loop fix: fix(session): stop prompt loop after model completion #18500
- Compaction loop fix: fix(session): stop prompt loop after model completion #19424
Reacted by Laventiy Flow, Ben Turner, Anh-Dung Nguyen and Prashant Dobariya
Metadata
Metadata
Assignees
Labels
No labels
Activity
phantomwhale commentedon May 15, 2026
Adding a clean reproduction on a different provider + model than the original report, in case it helps narrow down whether this is provider-specific or general.
Setup
1.14.47anthropic(direct, not via Copilot)claude-opus-4-7stepscap configuredWhat I observed
30 LLM iterations in a single user turn over 8m34s, with no
exiting loopever logged. The log file only ends because I killed the process — iteration would have continued.From
~/.local/share/opencode/log/, filtered toservice=session.prompt … loop:Cadence is 5–68s between iterations (mean ~17s), steady throughout — no plateau, no backoff curve.
What's ruled out from the log
Since past comments mentioned trouble reproducing, here's what the log shows is not happening during the loop:
session.statusevents oftype=retryERROR-level linespermission asking/question askingeventspermission=doom_loopeventssession.compactioneventssession.processorlinesmessage.part.updated, then proceededHappy to share artifacts
I have the full log (~100KB, 1121 lines) plus DB rows for that session. Glad to share via gist or DM if useful.
Dvalin21 commentedon May 16, 2026
Thanks for this — the cross-provider reproduction is really valuable. The 17s mean cadence with no backoff is consistent with what I saw.
One thing that would help narrow this: in those loop iterations, did the LLM response include actual content or tool calls, or was it producing near-empty responses each time? If there's a finish_reason field in your logs for each step, that would tell us whether the session loop is ignoring finish_reason=stop (same root cause as the original report) or if the model is actually returning finish_reason=end_turn but the processor isn't recognizing it.
phantomwhale commentedon May 18, 2026
Hmmm - log file doesn't appear to have any
finish_reasonentries in it. I'll try and add --log-level=DEBUG to future sessions and see if I pick up more useful outputgedeon1972-svg commentedon Jun 3, 2026
Hey! Dealing with those infinite retry loops on local models (like Qwen) is incredibly frustrating, especially when it just wipes the console on exit.
I was facing the exact same architectural flaw where the agent just blindly retries instead of failing gracefully. To fix this, I built a lightweight Python action called LoopGuardian.
It acts as a hard algorithmic brake. It audits the state and if it detects the exact same error 3 times in a row, it forces a hard stop, preventing the infinite context-bloat and saving your resources.
You might want to drop it in your workflow to catch those loops before they freeze your terminal: https://github.com/gedeon1972-svg/loop-guardian-skill
Hope it helps you get some control back over the agent!
fllsilva commentedon Jul 19, 2026
Confirming this is still happening on 1.18.3 with OpenCode Zen/Go
I ran into the exact same pattern described in this issue, on a clean environment:
OpenCode version: 1.18.3
Provider: OpenCode Zen / OpenCode Go
Plugins: none (removed all plugins/custom agents to rule them out)
Environment: reproduced both inside VSCode/Antigravity integrated terminal and in a native Windows CMD terminal — same behavior in both, so it's not terminal-related
Steps to reproduce:
Run opencode in a project folder with no AGENTS.md, no plugins, default PLAN/BUILD agents only
Run /init to generate AGENTS.md
Agent completes the file, then ends its turn with a text-only follow-up question ("The task is complete. What would you like to do next?")
Instead of stopping and waiting for user input, the session immediately restarts and re-runs the same /init prompt from scratch, in an endless cycle
This matches the root cause already identified in this thread — a text-only response with finish_reason=stop and no tool calls isn't being treated as "stop and wait for user," so the session processor calls the LLM again automatically.
Provider is the deciding factor: the loop only happens with Zen/Go. Switching the provider to the official DeepSeek provider — same project, same /init task, nothing else changed — resolved it completely: the task completed cleanly, ended its turn, and waited for input as expected, with no restart. So this looks isolated to how Zen/Go's streaming responses are parsed, not a general OpenCode session bug.
With every other provider I've used, this issue simply does not occur.
Cody-Coleman commentedon Jul 22, 2026
Can confirm this is still happening with 1.18.4, same scenario, same provider, same infinite loop.
fllsilva commentedon Jul 23, 2026
It must be clear that this is not a problem with OpenCode, it is with the free models of OpenCode Zen and the paid models of OpenCode Go as well.
Models from external providers, such as Deepseek, GLM, Kimi and others, work normally, this type of problem does not occur.
The problem is with the models provided by OpenCode Zem and Go, as far as I can tell.
uB4nshee commentedon Aug 10, 2026
I experienced a similar infinite-loop/token-consumption issue with Claude Sonnet 4.6, although the behavior in my case appears to be slightly different from the empty-LLM-response loop described here.
Environment
What happened
I asked OpenCode to analyze some changes in a local Helm repository.
During the analysis, OpenCode started repeatedly executing the same shell command:
The command itself was NOT hanging. It completed successfully and returned:
The OpenCode session nevertheless continued invoking the same operation repeatedly, resulting in an apparent tool-call loop.
This eventually consumed my entire daily token allowance before I could stop it.
I investigated whether RTK/Helm was causing the loop
The RTK OpenCode plugin is installed at:
I verified that RTK rewrites the command as expected:
I also tested both commands independently:
and:
Both commands complete normally.
The RTK plugin itself only calls
rtk rewriteand replaces the command when the rewritten command differs from the original. I could not find anything in the plugin that would cause the command to execute repeatedly.Therefore, at this point I suspect the loop is happening in the OpenCode/model tool-call cycle rather than in Helm or RTK.
Why I'm reporting this here
The behavior seems related to the tool/session loop described in this issue, especially because Claude Sonnet 4.6 is also involved in my case.
However, my case differs in an important way: instead of repeated empty LLM calls after tool execution, I observed the agent repeatedly requesting the same successful tool operation.
This also raises a broader concern about loop protection: a single model/tool interaction should not be able to repeatedly execute an identical tool call indefinitely and consume an entire token quota.
Would this be considered the same underlying session-loop issue, or should this behavior be tracked separately?
A configurable safeguard such as detecting repeated identical tool calls (e.g. N consecutive identical calls without progress) could also help prevent this class of issue and protect users from uncontrolled token consumption.
fidodido48 commentedon Aug 29, 2026
Same started happening to me since 2 days ago.
Big pickle model went into very verbose mode and cant seem to be able to turn it off via any opt. avail and is constantly looping same senstences over and over then stops working/goes idle unexpectedly.
Some
<invoke>and DSML cmds also appeared in the chat/block code till i put a rule for the agent to stop using these. It helped for a while to make it continue instead of stopping unexpectedly, but then it just started again after some time...opencode upgrade skipped: 1.18.25 is already installedBotxtrem commentedon Sep 1, 2026
se queda en loop Voy a escribir el script correcto.
Déjame escribir el script de inspección de match/live.
Déjame corregir el script.
Déjame escribir el script correcto de inspección.
Voy a escribir el script.
Déjame escribir el script de inspección.
Déjame corregir el script y ejecutarlo.
Déjame escribir el script correcto y ejecutarlo.
Déjame escribir el script de inspección y ejecutarlo ahora.
Voy a escribir el script correcto.
Déjame escribir y ejecutar el script de inspección.
Déjame corregir el script.
Escribo el script correcto y lo ejecuto.
Déjame escribir el script de inspección correcto.
Déjame reescribirlo correctamente.
Voy a reescribir el script de inspección ahora.
Déjame corregir y ejecutar.
Déjame escribir el script correcto.
Escribo el script.
Déjame escribir el script correcto de inspección y ejecutarlo de una vez.
Déjame escribir el script.
Escribo el script correcto.
Déjame escribir el script de inspección correcto y ejecutarlo.
Voy a escribirlo y ejecutarlo.
Déjame escribir el script correcto.
Escribo y ejecuto ahora.
Déjame escribir el script correcto de inspección y ejecutarlo.
Voy a hacerlo de una vez.
Déjame escribir el script correcto y ejecutarlo.
Escribo el script correcto y lo ejecuto ahora mismo.
Déjame escribir el script correcto de inspección.
Déjame escribirlo y ejecutarlo de una vez.
Escribo y ejecuto.
Déjame escribir el script correcto de inspección de match/live y ejecutarlo ahora.
No más vueltas: escribo y ejecuto.
Déjame escribir el script correcto y ejecutarlo.
devsers53 commentedon Sep 8, 2026
It repeats in a loop, 500 messages at a time; you can’t stop it using Esc — you can only restart the OpenCode app.
thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊