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
API 404 error silently hangs session in 'thinking' state with no error recovery #38951
When the upstream API returns an HTTP 404 error (e.g., model not found, wrong endpoint path, or deleted model), OpenCode silently hangs in the "thinking..." state indefinitely. No error message is shown to the user, no timeout or retry mechanism kicks in, and the session becomes completely unresponsive. The only recovery is to kill the process or restart the app.
This is distinct from stream errors during active streaming (#32366) — the 404 happens at the initial API request before any stream starts, yet produces the same stuck-in-thinking symptom.
OpenCode version
1.17.13 (also observed on earlier versions)
Steps to reproduce
Configure an OpenAI-compatible provider with an incorrect API endpoint (e.g., https://api.example.com/v1/chat/completions that returns 404)
Or use a model name that does not exist on the provider
Send any message to start a session
Observe: UI shows "thinking..." immediately but never transitions to either a response or an error state
The session stays stuck indefinitely — no cancel button, no error toast, no timeout recovery
Expected behavior
Show an error message when the API returns 404 (e.g., "Model not found" or "API endpoint unavailable")
Transition out of "thinking" state — either auto-retry with fallback or surface the error and return to idle
Client-side timeout to prevent indefinite stuck states when the API fails to respond properly
Additional context
The same class of issue has been reported for different error triggers:
This issue specifically covers the HTTP 404 trigger case, which may need a slightly different fix (the error occurs before stream initiation, so stream-level error handling does not apply).
After investigating the code, I traced the exact path that causes the infinite "thinking" state.
The Flow
Provider returns a timeout (e.g., nvapi HeaderTimeoutError) or 404
The error is caught by MessageV2.fromError() — for timeout errors it sets isRetryable: true
The session processor wraps the LLM stream with Effect.retry(SessionRetry.policy(...))
The retry policy checks retryable() — for retryable errors it returns a delay value
Critical bug: The no-headers backoff caps at RETRY_MAX_DELAY_NO_HEADERS = 30_000ms, but the OMO fallback guard threshold is RETRY_MAX_SINGLE_DELAY_MS = 60_000ms
Since 30_000 < 60_000, the condition OMO_HAS_FALLBACK && wait > RETRY_MAX_SINGLE_DELAY_MSnever evaluates to true for timeout errors
Effect.retry retries indefinitely (every ~30s) because the schedule has no upper bound on attempts
The session stays in "thinking" state forever
Root Cause Constants
packages/opencode/src/session/retry.ts:36
exportconstRETRY_MAX_SINGLE_DELAY_MS=60_000// too high — no-headers cap is 30_000
The protection mechanism that was designed to yield to OMO fallback when retry delays get too long is effectively dead code because the no-headers delay cap (30s) never exceeds the activation threshold (60s).
The Fix
Lower RETRY_MAX_SINGLE_DELAY_MS to match RETRY_MAX_DELAY_NO_HEADERS (30_000) so that once the backoff hits its cap for no-headers errors, the OMO fallback guard activates and the error propagates to Session.Event.Error, allowing configured fallback chains to take over.
Activity
UCASRMTG5D commentedon Jul 26, 2026
Root Cause Analysis
After investigating the code, I traced the exact path that causes the infinite "thinking" state.
The Flow
HeaderTimeoutError) or 404MessageV2.fromError()— for timeout errors it setsisRetryable: trueEffect.retry(SessionRetry.policy(...))retryable()— for retryable errors it returns a delay valueRETRY_MAX_DELAY_NO_HEADERS = 30_000ms, but the OMO fallback guard threshold isRETRY_MAX_SINGLE_DELAY_MS = 60_000msOMO_HAS_FALLBACK && wait > RETRY_MAX_SINGLE_DELAY_MSnever evaluates to true for timeout errorsEffect.retryretries indefinitely (every ~30s) because the schedule has no upper bound on attemptsRoot Cause Constants
packages/opencode/src/session/retry.ts:36The protection mechanism that was designed to yield to OMO fallback when retry delays get too long is effectively dead code because the no-headers delay cap (30s) never exceeds the activation threshold (60s).
The Fix
Lower
RETRY_MAX_SINGLE_DELAY_MSto matchRETRY_MAX_DELAY_NO_HEADERS(30_000) so that once the backoff hits its cap for no-headers errors, the OMO fallback guard activates and the error propagates toSession.Event.Error, allowing configured fallback chains to take over.I will open a PR with this fix shortly.
github-actions commentedon Sep 25, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.