Skip to content

Retry mechanism does not catch network TimeoutError (ETIMEDOUT / fetch timeout) #34672

Closed as duplicate

Description

@haki203

Bug summary

The built-in retry mechanism does not retry on network-level timeout errors. When a provider request times out (no HTTP response at all), the TimeoutError is treated as a non-retryable failure and the session fails immediately instead of backing off and retrying.

This particularly affects self-hosted / OpenAI-compatible providers where the downstream model is slow or overloaded but the network connection itself is established.

Expected behaviour

Network timeouts (TimeoutError: The operation timed out., ETIMEDOUT, fetch connect timeout) are transient by nature and should be retried with backoff, just like 5xx and rate-limit errors.

Actual behaviour

In packages/opencode/src/session/retry.ts, the retryable() function only matches:

  • SessionV1.APIError with isRetryable === true or statusCode >= 500
  • Plain-text messages containing "rate increased too quickly", "rate limit", "too many requests"
  • JSON error bodies with too_many_requests / rate_limit codes

A bare TimeoutError (no HTTP status, no JSON body, message "The operation timed out.") matches none of these branches, so retryable() returns undefined and the error is treated as permanent.

Logs

From ~/.local/share/opencode/log/opencode.log:

timestamp=2026-06-30T12:03:46.360Z level=ERROR run=d52fc4c9 message="stream error" providerID=glm modelID=glm/glm-5.2 error.error="TimeoutError: The operation timed out."
timestamp=2026-06-29T12:34:51.886Z level=ERROR run=aaef3725 message="stream error" providerID=glm modelID=glm/glm-5.2 error.error="AI_APICallError: [glm/glm-5.2] [502]: fetch connect timeout (reset after 30s)"
timestamp=2026-06-30T17:10:57.786Z level=ERROR run=e5462b05 message="stream error" providerID=glm modelID=glm/glm-5.2 error.error="AI_APICallError: [glm/glm-5.2] [502]: fetch connect timeout (reset after 11s)"

Notice: in the 502 case the upstream proxy returned fetch connect timeout, but in the TimeoutError case opencode itself timed out waiting for any response. Neither triggers a retry.

Reproduce

  1. Configure an OpenAI-compatible provider that proxies to a slow/overloaded model (e.g. a self-hosted gateway).
  2. Set "timeout": 100000 in provider options.
  3. Send a request while the downstream model is overloaded.
  4. After 100s with no response, opencode raises TimeoutError: The operation timed out. and the session fails — no retry attempt.

Suggested fix

In retryable(), also match timeout-like errors. Rough sketch:

// in retryable()
const msg = isRecord(error.data) ? error.data.message : undefined
if (typeof msg === "string") {
  const lower = msg.toLowerCase()
  if (
    lower.includes("rate increased too quickly") ||
    lower.includes("rate limit") ||
    lower.includes("too many requests") ||
    lower.includes("operation timed out")   ||   // new
    lower.includes("etimedout")              ||   // new
    lower.includes("fetch connect timeout")      // new
  ) {
    return { message: msg }
  }
}

Additionally, TimeoutError (or any error whose name/type indicates a transport timeout with no HTTP response) should probably be treated as retryable by default, since it is inherently transient.

Environment

  • opencode-ai: 1.17.11 (npm global, Windows x64)
  • Provider: custom OpenAI-compatible (@ai-sdk/openai-compatible)
  • OS: Windows 11

Activity

  1. github-actions commented on Jun 30, 2026

    @github-actions
    Contributor

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

  2. haki203 commented on Jun 30, 2026

    @haki203
    Author

    Closing as duplicate of #30611 — confirmed it already covers TimeoutError, ETIMEDOUT, and bare fetch timeouts failing the retryable classification in the session retry path. Subscribing to track the fix. Thanks for the heads-up bot.

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