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
Symptoms
- Process never exits:
opencode run hangs 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=stop or "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-codex instead of claude-sonnet-4.6 (works perfectly)
- Switch providers: Use different provider that sends proper stop signals
- Manual kill + restart:
Ctrl+C and 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 run as 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
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
opencode runhangs after completing tool calls — process never exits #17516 confirmed in v1.1.65 through v1.3.0Symptoms
opencode runhangs indefinitely after tool calls completeRoot Cause (from issue #17516)
OpenCode doesn't detect "done" state properly.
After the model finishes tool calls and produces final text response:
finish_reason=stopor "no tool calls in text-only response"Timeline from debug logs:
Pattern per step (steps 2-42):
Related Issues Found
opencode runhangs after completing tool callsReproduction Steps
Expected Behavior
After the model's final text response (post-tool-call), OpenCode should:
Actual Behavior
OpenCode:
Workarounds
gpt-5.3-codexinstead ofclaude-sonnet-4.6(works perfectly)Ctrl+Cand rephrase promptSuggested Fixes
Fix 1: Detect finish_reason=stop (Primary)
In
processor.ts, theprocess()function should return"stop"whenfinish_reason=stop:Fix 2: Break on Empty Responses
If LLM is called but produces NO tool calls and NO text, treat as done:
Fix 3: Add Loop Detection
Detect when same prompt step repeats without progress:
Fix 4: Add Timeout Protection
Add configurable timeout for headless mode:
{ "experimental": { "headless_timeout": 300000 // 5 minutes } }Environment
Impact
opencode runas subprocess hang foreverReferences
opencode runhangs after completing tool calls — process never exits #17516