Skip to content

API 404 error silently hangs session in 'thinking' state with no error recovery #38951

Closed as not planned

Description

@UCASRMTG5D

Description

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

  1. Configure an OpenAI-compatible provider with an incorrect API endpoint (e.g., https://api.example.com/v1/chat/completions that returns 404)
  2. Or use a model name that does not exist on the provider
  3. Send any message to start a session
  4. Observe: UI shows "thinking..." immediately but never transitions to either a response or an error state
  5. The session stays stuck indefinitely — no cancel button, no error toast, no timeout recovery

Expected behavior

  1. Show an error message when the API returns 404 (e.g., "Model not found" or "API endpoint unavailable")
  2. Transition out of "thinking" state — either auto-retry with fallback or surface the error and return to idle
  3. 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).

Operating System

macOS (darwin arm64)

Terminal

N/A (primarily affects Desktop GUI and TUI modes)

Activity

  1. UCASRMTG5D commented on Jul 26, 2026

    @UCASRMTG5D
    Author

    Root Cause Analysis

    After investigating the code, I traced the exact path that causes the infinite "thinking" state.

    The Flow

    1. Provider returns a timeout (e.g., nvapi HeaderTimeoutError) or 404
    2. The error is caught by MessageV2.fromError() — for timeout errors it sets isRetryable: true
    3. The session processor wraps the LLM stream with Effect.retry(SessionRetry.policy(...))
    4. The retry policy checks retryable() — for retryable errors it returns a delay value
    5. 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
    6. Since 30_000 < 60_000, the condition OMO_HAS_FALLBACK && wait > RETRY_MAX_SINGLE_DELAY_MS never evaluates to true for timeout errors
    7. Effect.retry retries indefinitely (every ~30s) because the schedule has no upper bound on attempts
    8. The session stays in "thinking" state forever

    Root Cause Constants

    packages/opencode/src/session/retry.ts:36

    export const RETRY_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.

    I will open a PR with this fix shortly.

  2. github-actions commented on Sep 25, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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