anomalyco / anomalyco/opencode

Anthropic overloaded_error becomes UnknownError and is not retried on native event path

Open
#39,340 2 comments 0 reactions 1 assignee View on GitHub

@jlongster is already working on this.

Since Jul 28, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description

Anthropic overload errors on the native LLM event path lose their structured retry metadata and terminate the turn instead of entering the existing session retry flow.

When Anthropic emits this SSE error:

{"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}

packages/llm/src/protocols/anthropic-messages.ts converts it to a provider-error event. However, the event has neither retryable: true nor an HTTP-equivalent status. Then packages/opencode/src/session/processor.ts handles every provider-error with:

case "provider-error":
  throw new Error(value.message)

This discards the event fields (classification, retryable, and provider metadata). MessageV2.fromError() consequently classifies the plain Error as UnknownError, and SessionRetry.retryable() does not retry the message overloaded_error: Overloaded.

The result is a terminal turn with no useful output. Manually sending another prompt normally succeeds because the provider failure is transient.

This is a regression of the behavior discussed in #20384. The earlier fix PR #20373 was closed without merge, and the current native event path still has the same outcome through a newer code path.

Expected behavior

Anthropic overloaded_error should be represented as a retryable API/provider error (HTTP-equivalent 529) and use the existing exponential-backoff retry flow. Structured provider-error fields should survive until error classification.

Actual behavior

The structured provider error becomes a plain Error, then UnknownError, so no retry occurs and the turn stops.

OpenCode versions
  • Observed in OpenCode 1.18.3.
  • Confirmed by source inspection to remain present in 1.18.8 (latest release at the time of reporting).
Steps to reproduce
  1. Use the native Anthropic Messages route with an Anthropic model.
  2. Receive an Anthropic SSE error event whose error.type is overloaded_error.
  3. Observe that the turn stops instead of showing retry status and retrying.

This can be made deterministic in a protocol/processor test by feeding the SSE event above into the Anthropic stream parser and then draining the resulting provider-error through SessionProcessor.process().

Suggested fix
  1. In anthropic-messages.ts, map error.type === "overloaded_error" to a provider event with retryable: true (and preserve the raw type in provider metadata).
  2. In processor.ts, replace throw new Error(value.message) with a typed provider-stream error that preserves retryable, classification, and metadata.
  3. In MessageV2.fromError(), convert that typed error to APIError; map Anthropic overload to statusCode: 529 and isRetryable: true.
  4. Add a regression test asserting that the SSE payload above reaches SessionRetry as retryable rather than UnknownError.
Relevant files
  • packages/llm/src/protocols/anthropic-messages.ts (onError)
  • packages/llm/src/schema/events.ts (ProviderErrorEvent)
  • packages/opencode/src/session/processor.ts (handleEvent("provider-error"))
  • packages/opencode/src/session/message-v2.ts (fromError)
  • packages/opencode/src/session/retry.ts (retryable)
Environment
  • Provider: Anthropic
  • Example model: Claude Opus
  • OS: macOS

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.