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)
Problem
Requests for OpenCode Zen free models (e.g.
deepseek-v4-flash-free) routed through routatic-proxy fail with HTTP 429FreeUsageLimitError("Rate limit exceeded. Please try again later."), so the proxy ultimately returns502 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 setsContent-Type,Authorization,x-api-key(andAcceptfor streams) but neverUser-Agent. opencode.ai's Zen free-tier rate limiter keys its bucket by client identity: requests whoseUser-Agentstarts withopencode/are exempt; any generic client UA is throttled.Evidence
Reproduction with one account key against
https://opencode.ai/zen/v1/chat/completions, modeldeepseek-v4-flash-free(also reproduced via/v1/messagesand/v1/responses):Go-http-client/1.1(what the proxy sends)FreeUsageLimitErrorcurl/8.xFreeUsageLimitErroropencode/1.18.18/opencode/1.0.0/opencode/9.9.9/opencode/routatic-proxySource citation:
internal/provider/opencode_zen.go(executeAnthropic,streamAnthropic,doRequest,doJSONRequest) andinternal/provider/opencode_go.go(executeAnthropic,streamAnthropic,doRequest) construct upstreamhttp.Requests and set auth/accept headers but neverUser-Agent; Go'snet/httpthen sends the defaultGo-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
FreeUsageLimitErrorbucket → proxy falls back to paidopencode-gomodels (or returns 502). The native opencode client is exempt, so the same account/model succeeds there.Contract violated:
CONFIGURATION.md/MODELS.mdadvertise 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-proxyon 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 aUser-Agentwith theopencode/prefix). Checkinternal/client/opencode.gofor 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)