Skip to content

[bug] Zen free models 429 FreeUsageLimitError because upstream requests use generic Go User-Agent #137

Description

@cheerc

Problem

Requests for OpenCode Zen free models (e.g. deepseek-v4-flash-free) routed through routatic-proxy fail with HTTP 429 FreeUsageLimitError ("Rate limit exceeded. Please try again later."), so the proxy ultimately returns 502 all models failed. The same account key + same model works fine from the opencode TUI/CLI.

Root cause: upstream requests from the proxy carry Go's default User-Agent: Go-http-client/1.1 — the provider code sets Content-Type, Authorization, x-api-key (and Accept for streams) but never User-Agent. opencode.ai's Zen free-tier rate limiter keys its bucket by client identity: requests whose User-Agent starts with opencode/ are exempt; any generic client UA is throttled.

Evidence

Reproduction with one account key against https://opencode.ai/zen/v1/chat/completions, model deepseek-v4-flash-free (also reproduced via /v1/messages and /v1/responses):

User-Agent Result
Go-http-client/1.1 (what the proxy sends) HTTP 429 FreeUsageLimitError
curl/8.x HTTP 429 FreeUsageLimitError
opencode/1.18.18 / opencode/1.0.0 / opencode/9.9.9 / opencode/routatic-proxy HTTP 200

Source citation: internal/provider/opencode_zen.go (executeAnthropic, streamAnthropic, doRequest, doJSONRequest) and internal/provider/opencode_go.go (executeAnthropic, streamAnthropic, doRequest) construct upstream http.Requests and set auth/accept headers but never User-Agent; Go's net/http then sends the default Go-http-client/1.1.

Producer→consumer analysis: the proxy (producer) forwards the raw request with a generic UA → Zen free tier (consumer) classifies it as a non-opencode client and applies the FreeUsageLimitError bucket → proxy falls back to paid opencode-go models (or returns 502). The native opencode client is exempt, so the same account/model succeeds there.

Contract violated: CONFIGURATION.md / MODELS.md advertise OpenCode Zen free models (e.g. nemotron-3-ultra-free, mimo-v2.5-free, deepseek-v4-flash-free) as usable fallback models; the Zen free tier grants them to the opencode client identity, of which the proxy is a documented consumer.

Verified fix: setting User-Agent: opencode/routatic-proxy on the upstream requests resolves it (HTTP 200, free model completes through the proxy).

Scope

internal/provider/opencode_zen.go, internal/provider/opencode_go.go — upstream request construction (set a User-Agent with the opencode/ prefix). Check internal/client/opencode.go for other upstream paths.

Expected Outcome

Requests for Zen free models via the proxy complete (HTTP 200) instead of 429/502, attributed to the same account as the native opencode client.

Related

Related: #101 (usage-limit fallback behavior), #91 (502 all models failed)

🤖 opened by: agent (opencode-a10ab0)

Metadata

Metadata

Assignees

No one assigned

    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