Repository navigation
Description
Feature hasn't been suggested before.
- I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
- I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
The free models (opencode/minimax-m3.1-flash-free and the other *-free models) have no documented accounting rules. Four things a user cannot answer from the docs today:
1. The Go pricing table says "Unlimited" — but a daily cap exists.
OpenCode Go lists MiniMax M3.1 Flash Free as Free / Unlimited (monthly limit) and Unlimited / Unlimited / Unlimited in the "Estimated requests" table. In reality the free models are subject to a daily request cap that surfaces as FreeUsageLimitError (HTTP 429, retry-after up to 24 h — PR #49195), reported as roughly 200 requests/day (#33495, #33318, #15585). The docs never mention this cap, so "Unlimited" reads as "no limit". This row is the one statement I found that is simply wrong.
2. Is idle wall-clock time charged?
Nothing in the docs says. The limiter decrements per request, so an open idle session appears to consume nothing — but "5-hour — 20% of the monthly limit" is easy to read as a wall-clock window that runs while the session is open. Please state explicitly that free-tier accounting is exclusively request/token based and that idle time is not charged.
3. Pool scope: does switching models give me a fresh allowance?
The behaviour differs per regime and is not documented:
- Free models (in-app): the bucket is per IP / per API key and decremented per request on a daily cycle (
packages/console/app/src/routes/zen/util/handler.ts:createIpRateLimiter(modelInfo.id, modelInfo.rateLimit, ip, ...),createKeyRateLimiter(...),createTrialLimiter(modelInfo.trialProvider, ip)). Reports indicate that all free models fail at the same time once it is exhausted, i.e. the bucket appears shared across free models — switching fromminimax-m3.1-flash-freetoglm-5.3-flash-freewould not reset it. Please confirm. - Free models vs. Zen balance: separate pools. A maintainer comment on #45324 states free models "never use your Zen balance, so adding credits doesn't lift that limit". The paid balance itself is shared across models (
BillingTable.balance,monthlyUsageper workspace and per user). Please document that the two are independent. - Go plan: "Each model has the following usage limits: 5-hour — 20% … weekly — 50% … monthly — 100%" implies a per-model meter, so switching models switches meters. Please confirm.
4. There is no way to see the remaining allowance.
Go subscribers can "track your current usage in the console". For the in-app free models (no console account) there is no documented place to look; the error body is only "Rate limit exceeded. Please try again later." with a retry-after header. Please expose the remaining allowance (e.g. a response header and/or a console page) and document it.
What I would like to see
A "Free model limits" section in the docs that states, unambiguously:
- free-tier accounting is exclusively request/token based and idle time is not charged;
- the size of the daily allowance, what it meters, its reset window, and its scope (IP / API key / account / workspace);
- whether that allowance is shared across free models and independent of the paid Zen balance;
- how Go's per-model limits relate to a shared pool, if any;
- where the user can see the remaining allowance.
Notes
- This is about documentation and visibility, not about changing the cap. The cap behaviour is already tracked in #33318, #33495, #45324, #43208 and #42938.
- Environment: OpenCode app, model
opencode/minimax-m3.1-flash-free, Linux. Observed: no requests are issued while a session is idle; after the allowance is exhausted, all free models fail simultaneously withFreeUsageLimitError/ HTTP 429, and adding Zen credit does not change that.
Activity
opencode-agent commentedon Oct 20, 2026
Thanks for laying this out so clearly, @tomsun66-star!
As I understand it, you'd like a "Free model limits" section in the docs that covers:
You'd also like the "Unlimited" entry for free models in the Go table corrected, since a daily cap does apply. The maintainers will consider it.
Related issues, starting with the closest:
Retry-After/x-ratelimit-*headers on 429 responses, which covers your points 2 and 5Eriol1805 commentedon Oct 25, 2026
Independent reproduction: rejection is per-request, not account-global (Windows + TUI +
mimo-v2.6-flash-free)Data point for the "pool scope" question above, reproduced from a different OS/client/model than #58751.
Same account, same machine, same minutes of 2026-10-25 (UTC), both sessions verified identical via
session.list—providerID: opencode,model: mimo-v2.6-flash-free,variant: default:GET /api/session/{id}/context)ses_09d7744…(older, long-running)Free usage limit reachedon every promptses_e8aaa88…(new session)Server log (
~/.local/share/opencode/log/opencode.log) — everyQuotaExceededbelongs to the old session only, including attempts made while the new session was receiving successful responses:No
QuotaExceededentry exists for any other session in the log. The client-facing message in the blocked session is:You've used today's free messages. They reset at 21:00.Environment: Windows 11, opencode v2.1.4, TUI,
opencode/mimo-v2.6-flash-free, free tier only (no Go, $0 balance).This matches the request-size behavior in #58751 (closed as billing): the account was not blocked — a ~2.6× larger assembled context was rejected in the same minutes a small request succeeded. Suggested answer for the documentation requested here: rejection is not applied account-wide; it tracks the individual request (context size), so a new session keeps working while the old one stays refused, and the error text ("today's free messages") misdescribes what is actually being measured.
Eriol1805 commentedon Oct 27, 2026
Follow-up: reproduced upstream — the discriminator is the session ID header, and Go's endpoint accepts all session IDs
Continuing my previous comment. Environment: v2.1.4, Windows (a second machine on Fedora does not reproduce this — same account).
1. A/B matrix at the CLI level (interleaved, same minute, same model)
session.generateresultses_e85491603ffeaKOUQZjBbdWTN1session.createwithoutid)200 OKses_abcdef0123456789abcdsession.createwith customid(20 hex)FreeUsageLimitErrorses_e8aaa88caffeTEST1234abcdsession.createwith customid(server-like format)ses_e8aaa88caffeAAAAbcdefGHIjsession.createwith customid(26 chars: 12 hex + 14 base62)session/importsessioninfo.idKey points:
time.*,agent,model,outcome,title,projectID, message content, or the account's global quota (the server-generated ID succeeds interleaved with failures at the same instant).idandslug(random title slug).POST /api/sessionand then blocked at generate time.session.fork/session.compactdon't hit the gate (they never reach generate).2. Captured the real upstream request (local MITM proxy)
I pointed the
opencodeprovider at a local forwarding proxy and let the CLI issue its real request. Both bodies are byte-identical (57,301 B); the only difference between a working and a failing request is:Full real headers also include
Authorization: Bearer st_…(a session token — not theoc_sk_fromauth.jsonnor the accountsk-key),x-opencode-org-id: wrk_…,x-opencode-project,x-opencode-client: cli,b3,traceparent.Replaying those exact requests (real token + org + all headers) directly against
https://opencode.ai/inference/openai/v1/chat/completionsreturns:for all three variants (no session headers, custom session id, server-generated session id). Note this replay was captured after the free daily allowance had been exhausted during my testing; the 200-vs-503 discrimination by session id was observed at CLI level before exhaustion, with the byte-identical payloads/headers shown above (also captured with a local echo provider).
3. What OpenCode Go changes
The Go endpoint
https://opencode.ai/inference/go/openai/v1behaves differently:400 MissingSessionID— "Request is missing x-opencode-session and cannot be routed efficiently" (so upstream routes by session id there).x-opencode-session+ real token:200 OKfor BOTH a server-generated and a custom/imported session id.After activating OpenCode Go, the CLI with provider
opencode-gogenerates successfully for both ID types (session.generateon a custom-ID session returns{"data":{"text":"OK"}}), while the free provider (/inference/openai/v1) still returns429 FreeUsageLimitErrorfor everything (free daily allowance exhausted during this testing —Retry-After≈ 23.5 h, i.e. it expires ~00:00 UTC).Working hypothesis
On the free endpoint, quota/routing appears to be keyed per
x-opencode-session*: sessions whose id was not produced by the server's ownsession.createpath land in a bucket that always answersFreeUsageLimitError, regardless of the account's actual state. The Go endpoint has no such discrimination.Questions for maintainers:
session.createwithid,session/import) ids registered differently from server-generated ones?experimental.session.importand custom session ids are unusable on the free tier.Happy to run more experiments — I have the exact upstream requests (bodies + headers) captured for both cases.
Timestamps for reference (UTC): 2026-10-25 16:28, 23:06, 23:10, 23:14; 2026-10-26 02:19–02:27.
neriousy commentedon Nov 12, 2026
Free-tier questions and problems go in #59387. Closing this one in favor of that thread.