Skip to content

bug: UI stuck on 'thinking' indefinitely after stream error, no error displayed or state recovery #32366

Closed as not planned

Description

@Superissac

Issue

When a stream error occurs (e.g., AI_APICallError, socket closed unexpectedly), the desktop UI gets stuck showing "thinking..." indefinitely. No error message is displayed, no state recovery happens, and the session becomes unusable. The user must restart the app to recover.

Root Cause Analysis

After analyzing multiple server logs from debug dumps:

  1. Stream errors are frequent but silently swallowed

    • AI_APICallError: <none> — empty error object, no details from the upstream API
    • AI_APICallError: The socket connection was closed unexpectedly — upstream proxy terminates the SSE stream mid-response
    • These errors are logged on the server side but never propagated to the UI
  2. UI state machine never transitions out of 'thinking'

    • After a stream error, the server logs show the session is dead (no further stream events)
    • But the desktop UI continues displaying "thinking..." with no cancel button or error indicator
    • The frontend apparently waits for a stream event that will never come
  3. No automatic retry or timeout

    • No retry mechanism after stream failure
    • No client-side timeout to abort stuck states
    • User must manually cancel or restart the app
  4. Server log stops processing

    • In one dump, the last log entry at 2026-06-15T01:46:02Z was a cancel event
    • After that, zero log entries despite the UI showing "thinking"
    • This confirms the server stopped processing but the UI never received this state change

Server Log Evidence

# Frequent silent stream errors (no error detail)
level=ERROR message="stream error" providerID=YerPlan modelID=qwen3.6-plus error.error="AI_APICallError: <none>"

# Socket-level failures
level=ERROR message="stream error" error.error="AI_APICallError: Cannot connect to API: The socket connection was closed unexpectedly"

# After stream errors, server stops responding but UI stays 'thinking'
level=INFO message=cancel session.id=ses_13898e204ffemfGewCcp9BrPCN
# ← zero further log entries after this point (01:46 UTC)

Steps to Reproduce

  1. Connect opencode desktop to an API provider via an openai-compatible proxy (e.g., https://api.xxx.com/v1)
  2. Use a worktree session on a local project directory
  3. Send a message that triggers a long-running response
  4. Wait for the upstream proxy to drop the connection (intermittent, but happens frequently with unstable proxies)
  5. Observe: UI stuck on "thinking...", no error shown, session unresponsive

Expected Behavior

  1. Display an error message when the stream fails (e.g., "Connection lost. Retry?")
  2. Provide a "Retry" or "Cancel" button
  3. Implement a client-side timeout to prevent indefinite stuck states
  4. Auto-recover when the server stops responding

Environment

  • opencode: 1.17.7 (Desktop app)
  • OS: macOS darwin arm64 (M-series)
  • Electron: 42.3.3 / Chrome 148
  • Provider: openai-compatible proxy (not direct Anthropic/OpenAI)
  • Model: qwen3.6-plus (via proxy)
  • Node: 24.15.0

Related Issues

Activity

  1. github-actions commented on Jun 15, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

  2. Superissac commented on Jun 15, 2026

    @Superissac
    Author

    Updated findings after deep investigation

    API works fine (direct curl test)

    curl -X POST https://api.qiyue999.com/v1/chat/completions \
      -d '{"model":"qwen3.6-plus","messages":[{"role":"user","content":"Hello"}],"stream":true}'
    # Returns valid SSE stream with reasoning_content chunks - works perfectly

    But opencode CLI gets stuck even with --pure (no plugins)

    opencode --pure /Users/issac/ -m YerPlan/qwen3.6-plus
    # Stuck on "thinking..." forever, server logs show stream starts but never completes

    Root cause hypothesis

    The API returns stream chunks with reasoning_content in the delta:

    {"choices":[{"delta":{"content":null,"reasoning_content":"Here's a thinking process..."}}]}

    But @ai-sdk/openai-compatible may be expecting reasoning in a different format (e.g., Anthropic-style thinking blocks or OpenAI-style reasoning object). The adapter likely never recognizes the stream as complete when processing reasoning_content from Qwen models, causing the agent loop to hang indefinitely.

    This is NOT:

    • A plugin issue (--pure same result)
    • A desktop UI-only issue (CLI same result)
    • An API availability issue (direct curl works)
    • A thinking configuration issue (thinking: {type:"disabled"} didn't help)

    Likely fix

    The @ai-sdk/openai-compatible adapter needs to properly handle reasoning_content in stream chunks from qwen-compatible endpoints. The current implementation may treat it as an incomplete or malformed response type that never transitions to the text phase.

  3. marceloj commented on Jul 6, 2026

    @marceloj

    I'm experiencing the same issue on OpenCode Desktop v1.17.13 on Windows. The UI gets stuck on "thinking..." indefinitely after a stream error, with no error message or recovery. The only way to fix it is to restart the app.

    I've reproduced this with 3 different models/providers:

    • Mimo v2.5
    • DeepSeek (DeepSeek-v4-flash)
    • (a third model)

    The issue happens with all of them, so it's not model-specific — it's a bug in the desktop app's stream error handling. The session becomes completely unresponsive and there's no cancel/retry option.

    Happy to provide debug logs if needed.

  4. H3XDaemon commented on Jul 12, 2026

    @H3XDaemon

    Hit this same permanently-stuck state from a different trigger (a database schema error, #35986) and traced the full path, which might be useful here since a frontend fix alone can't cover it.

    The client sets session_status to busy optimistically on submit (packages/app/src/components/prompt-input/submit.ts). prompt_async returns 204 and forks the actual work; when that fork fails, the catch in packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts logs the cause and publishes Session.Event.Error — but nothing on this path ever publishes an idle status. The only place that resets status on failure is the v1 processor's halt chain (packages/opencode/src/session/processor.ts, catchCauseIf → halt → error event + status.set idle), and failures that happen before the stream starts never reach it. The v2 runner side is worse: its coordinator only logs defects (packages/core/src/session/execution/local.ts) with no terminal event at all.

    So the spinner waits on a status change that no code path will ever send. In my case the session was also unrecoverable across restarts because the underlying SQL failure was deterministic — every new prompt died the same way. Log line for reference, note the error message itself is lost to serialization (that part is #35986):

    ERROR ... session.id=ses_... cause={"_id":"Cause","failures":[{"_tag":"Die","defect":{"code":"ERR_SQLITE_ERROR","errcode":1,"errstr":"SQL logic error"}}]} prompt_async failed
    
  5. JonatanGarbuyo commented on Aug 4, 2026

    @JonatanGarbuyo

    I can reproduce the same stuck-state path with the direct OpenAI provider, not an OpenAI-compatible proxy.

    Environment

    • OpenCode when the incident occurred: 1.18.11 (currently 1.18.13)
    • OS: Linux
    • Provider/model: openai/gpt-5.6-terra
    • UI: terminal (not Desktop)

    Observed sequence

    1. A normal agent request started.
    2. OpenCode logged AI_APICallError: <none> after about 43 seconds.
    3. It automatically started a new stream attempt about 2 seconds later.
    4. That follow-up request emitted its tool results, then remained pending with no completion, error, or usable UI state for about 4 minutes 13 seconds.
    5. Manual cancellation was required; OpenCode recorded the request as Aborted.
    6. The session data was intact and accepted a later message, so this was not session corruption.

    Sanitized log excerpt:

    level=ERROR message="stream error" providerID=openai modelID=gpt-5.6-terra error.error="AI_APICallError: <none>"
    level=INFO message=stream providerID=openai modelID=gpt-5.6-terra
    # tool results complete; no terminal stream event for ~4m13s
    level=INFO message=cancel session.id=<redacted>
    level=ERROR message=process messageID=<redacted> error=Aborted
    

    The automatic retry is noteworthy: it recovered the initial failed stream but could still leave the subsequent request permanently pending. A timeout/error transition after the retry, plus a visible retry/cancel action in TUI, would prevent the session from appearing unrecoverable.

  6. aentrepreneur commented on Aug 16, 2026

    @aentrepreneur

    Confirming this occurs with the built-in opencode provider too (not only openai-compatible proxies).

    OpenCode 1.18.18 (Linux/WSL2), providerID=opencode modelID=big-pickle shows stream transport errors like:

    timestamp=2026-08-16T23:51:13.948Z level=ERROR run=042c53b4 message="stream error" providerID=opencode modelID=big-pickle ... error.error="AI_APICallError: Cannot connect to API: The socket connection was closed unexpectedly ..."
    

    User-visible symptom: the session can “lose the connection” without a clear on-screen error (silent drop / no explicit terminal error state), matching the core bug described here.

    I also opened a specific issue focused on opencode/big-pickle + socket-closed failures and linked it back here.

  7. andrew222651 commented on Aug 23, 2026

    @andrew222651

    The web interface has the same problem

  8. Animo78 commented on Oct 4, 2026

    @Animo78

    Got the same issue :(

  9. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

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