Skip to content

provider: quota limit surfaced as "not valid JSON" / Authentication instead of the real message #50352

Open

Description

@herjari

Summary

When an OpenCode Go account exceeds its weekly model usage limit, requests fail with a misleading error: AI.Error.Authentication: Error from provider (Console Go): Upstream request failed: [server_error] Upstream response was not valid JSON. The real, actionable message (Weekly usage limit reached… enable usage from your available balance…) only appears in the server log, and the failure is classified as an authentication/JSON error instead of a quota/rate-limit error. Affected sessions look broken and the actual cause is hard to find.

Environment

  • opencode version: 2.0.11 (OpenCode Desktop; bundled CLI reports opencode v2.0.11). The separately installed npm CLI is V1 1.18.18.
  • OS: Windows 11 (NT 10.0.26200)
  • Terminal: OpenCode Desktop app on Windows (no TUI)
  • Shell: cmd.exe / PowerShell
  • Install/channel: Desktop release (ai.opencode.desktop), bundled CLI 2.0.11
  • Active plugins: a local gates.ts plugin file exists in the global config plugins/ directory; unrelated to this issue.
  • opencode service status: http://127.0.0.1:49374

Reproduction

  1. Use a provider account (OpenCode Go) whose weekly usage limit for a model is exhausted.
  2. Send a prompt to any session using that provider/model (e.g. opencode-go/<model>#max).
  3. The session fails with AI.Error.Authentication: Error from provider (Console Go): Upstream request failed: [server_error] Upstream response was not valid JSON.
  4. In ~/.local/share/opencode/log/opencode.log, the same time window shows the real cause: AI.Error: Weekly usage limit reached. Resets in 1hr 13min. To continue using this model now, enable usage from your available balance: https://opencode.ai/....

Expected Behavior

The client should surface the provider's actual message (weekly usage limit / quota exhausted) and classify it as a quota or rate-limit error, not as an authentication failure or invalid JSON.

Actual Behavior

AI.Error: Error from provider (Console Go): Upstream request failed: [server_error] Upstream response was not valid JSON
  [cause]: AI.Error.Authentication: Error from provider (Console Go): Upstream request failed: [server_error] Upstream response was not valid JSON

The quota message is only visible in the log. Around the same window the log also shows: WARN failed to load OpenCode provider config cause="HttpClientError: StatusCode: non 2xx status code (401 GET https://opencode.ai/console/api/v2/config)".

Additional Context

  • Frequency: intermittent while the limit is active (some requests succeed, most fail), across multiple sessions.
  • Impact: the UI shows an auth/parse error; users cannot tell the account is out of quota and retries look like random flakiness.
  • Workaround: switch to another credential/account or provider, enable balance usage, or wait for the weekly reset.
  • Likely cause: the provider's quota response body is not JSON and the client's error mapping wraps it as AI.Error.Authentication.
  • Sanitized log lines (credentials and session ids removed):
    • level=ERROR message="Failed to drain Session" cause="AI.Error: Error from provider (Console Go): Upstream request failed: [server_error] Upstream response was not valid JSON"
    • level=ERROR message="Failed to drain Session" cause="AI.Error: Weekly usage limit reached. Resets in 1hr 13min. To continue using this model now, enable usage from your available balance: https://opencode.ai/..."

Activity

  1. jinhuang712 commented on Sep 21, 2026

    @jinhuang712

    Adding a data point with the same message but a different signature (403 provider.auth, transient recovery), in case it is a separate root cause.

    Environment

    • opencode v2.0.12, macOS (Darwin 25.5.0, arm64), zsh
    • Provider opencode-go (Go subscription), model deepseek-v4.1-flash (observed on both #max and default variants)

    What I saw

    Five failures across four sessions between 2026-09-21 12:13 and 17:44 UTC. The persisted assistant error is identical each time:

    {"type":"provider.auth","message":"Error from provider (Console Go): Upstream request failed: [server_error] Upstream response was not valid JSON","status":403}

    The outer error comes from SessionRunner.drain and wraps AI.Error.Authentication, so the turn dies immediately. Because it is classified as provider.auth (non-retryable), no retry is attempted even though the upstream body carries server_error.

    Findings

    • Upstream request failed: [server_error] Upstream response was not valid JSON does not exist anywhere in the client repo. It is produced upstream of the gateway; the gateway only adds the Error from provider (Console Go): prefix (packages/console/app/src/routes/zen/util/handler.ts:337), which means the gateway reached an upstream that answered 403 with a JSON error body.
    • Requests must carry x-opencode-session. Without it the gateway answers 400 MissingSessionID (Request is missing x-opencode-session and cannot be routed efficiently). Routing is session-derived, so a bad upstream can stay sticky to specific sessions.
    • Currently not reproducible. Fresh requests all succeed: 24 requests on 2026-09-22 (non-stream, stream, with tools, 12 different session shards, and a ~67k-char prompt with "reasoning_effort":"max") returned HTTP 200. So on my side this looks like a transient upstream window rather than a persistent quota state. Responses show "cost":"0".
    • Client-side note: the message contains server_error, which the current retry patterns match (packages/opencode/src/session/retry.ts:36). If v2.0.12 reclassifies this to provider.auth before the retry decision is evaluated, that would explain the missing retry. Worth checking whether the retry decision runs before or after provider-auth classification.

    Not claiming this is the quota-masking case described above — the 403 + provider.auth + recovery-without-a-quota-reset looks different — but recording it here rather than opening a duplicate.

  2. 4Liberty commented on Sep 23, 2026

    @4Liberty

    Answering the open question in @jinhuang712's data point — whether the retry decision runs before or after provider-auth classification: classification runs first, and it wins.

    Measured on 2.0.9–2.0.14: the 403 becomes reason tag Authentication, the stored part is provider.auth, and the retry predicate returns false for Authentication (after checking x-should-retry first). The retryable message patterns, which do match server_error, are never consulted on this path. A guard that stamps x-should-retry: true on exactly this class makes the client retry and the turn completes — so the retry loop is fine.

    Full mechanism plus a 22-failure measurement over three days is in #40194.

  3. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed report and the log lines. We couldn't reproduce this here: it needs an OpenCode Go account whose weekly limit is used up, and we don't have one in this environment.

    From the code: OpenCode already treats Go usage-limit responses as quota errors and shows the provider's message, as long as the response comes back as a quota or rate-limit error (402, or 429 with a usage-limit code or message). That's why the Weekly usage limit reached… error appears for some of your requests. The failing requests look different: the AI.Error.Authentication label means the Go gateway itself answered with a 401/403 whose body only said Upstream response was not valid JSON, so the app had nothing about the quota to show. The 401 on the console config request in the same window points the same way.

    So this most likely needs fixing in how the OpenCode Go service responds when the limit is reached, and that needs a maintainer with access to it. If you can catch the raw HTTP status and body of one failing request, adding it here would help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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