Skip to content

provider: legacy V1 provider.opencode-go block silently dropped in v2 breaks Go credentials (fixed by removing the block) #54370

Closed as not planned

Description

@yindongfang509

Description

Summary

After upgrading an existing install to opencode v2, OpenCode Go stopped working with Missing API key / Invalid credential on every Go model. The root cause was a legacy V1-format provider block (provider.<id> singular key with npm + options) that v2 silently skipped during config normalization, while the modern providers.<id> / OAuth credential path took over. Removing the legacy block alone fixed everything — all 31 Go models work again without any other change.

Environment

  • opencode version: 2.0.26 (latest)
  • OS: Microsoft Windows NT 10.0.19045.0 (win32 x64)
  • Terminal: desktop app (OPENCODE_CLIENT=desktop, xterm-256color)
  • Shell: cmd.exe
  • Install/channel: latest
  • Active plugins: none

Reproduction

  1. Start from a v1-era config where opencode.json contains a legacy block: "provider": { "opencode-go": { "npm": "@ai-sdk/anthropic", "options": { "apiKey": ..., "baseURL": "https://opencode.ai/zen/go/v1" }, "models": { ... } } }
  2. Upgrade to v2. Startup logs report the block was skipped: configuration normalization diagnostic path=$.providers.opencode-go kind=invalid action="skipped malformed recognized value".
  3. Start a session with any opencode-go/* model → fails with AI.Error: Missing API key (or on desktop connect: Invalid credential / Insufficient account funds).
  4. Delete the legacy provider.opencode-go block, keep everything else (including the OAuth opencode credential in auth.json/database).
  5. Restart → all Go models work. No providers.opencode-go entry needed.

Expected Behavior

Legacy V1 provider blocks should either be migrated correctly or removed with a clear diagnostic, and the OAuth/credential path used by opencode-go should not be shadowed by a silently-dropped legacy block.

Actual Behavior

v2 silently drops the legacy block (WARN-only), leaving the config in a broken half-migrated state: credentials stored under the OAuth opencode integration instead of opencode-go, so the Go models can never find their key.

Additional Context

Plugins

No response

OpenCode version

No response

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report, and good to hear that removing the block got Go working again.

    We'd like to reproduce this, but two parts of the report don't quite fit together:

    • The warning you quote is for $.providers.opencode-go (plural). A problem in a legacy provider.opencode-go block would be reported at $.provider.opencode-go. So that warning may be about the providers.opencode-go entry you wrote by hand, not the legacy block.
    • If the legacy block had really been dropped, deleting it shouldn't have changed anything. It may instead have been migrated and replaced the built-in Go provider.

    To sort this out, could you please share, in your own words and briefly:

    1. The legacy provider.opencode-go block exactly as it was. Please replace the key with REDACTED, but tell us whether it was a literal key, {env:...} or {file:...}, and include the full models section.
    2. The exact startup diagnostic lines (with the full path=) and the exact error you saw, with only the legacy block in the config (no hand-written providers.opencode-go).
    3. Which credentials you had connected at the time (OpenCode Console, OpenCode Go key, or both).

    You can reply here or edit the issue. If you edit it, please also leave a comment saying you've updated it, so we know when you're done.

    These existing issues may be related, ordered from most to least likely:

  2. yindongfang509 commented on Oct 10, 2026

    @yindongfang509
    Author

    You're right on both counts — I conflated two different diagnostics, and your second hypothesis is the correct one. Here are the exact facts:

    1. The V1 block as it was (apiKey replaced with REDACTED; it was a literal key with sk- prefix, not {env:...}/{file:...}):

    "provider": {
    "opencode-go": {
    "models": {
    "deepseek-v4-flash": {
    "name": "DeepSeek V4 Flash",
    "reasoning": true,
    "variants": {
    "high": { "thinking": { "budgetTokens": 8192, "type": "enabled" } },
    "low": { "thinking": { "budgetTokens": 2048, "type": "enabled" } },
    "max": { "thinking": { "budgetTokens": 16384, "type": "enabled" } },
    "medium": { "thinking": { "budgetTokens": 4096, "type": "enabled" } },
    "none": { "thinking": { "type": "disabled" } }
    }
    },
    "qwen3.8-max": { /* same shape as above */ }
    },
    "name": "OpenCode Go",
    "npm": "@ai-sdk/anthropic",
    "options": {
    "apiKey": "REDACTED",
    "baseURL": "https://opencode.ai/zen/go/v1"
    }
    }
    }
    2. The actual startup diagnostics with only this legacy block present (nothing with path=$.providers.opencode-go, only the singular form):

    level=WARN message="configuration normalization diagnostic" path=$.provider.opencode-go.models.deepseek-v4-flash.reasoning kind=unsupported action="omitted unsupported legacy setting"
    level=WARN message="configuration normalization diagnostic" path=$.provider.opencode-go.models.qwen3.8-max.reasoning kind=unsupported action="omitted unsupported legacy setting"
    And the session failure with the legacy block in place: AI.Error: Missing API key. So you are correct — the block was never skipped. It was accepted with the reasoning field stripped, and shadowed/replaced the built-in Go provider (the skipped malformed recognized value warning I quoted in the original post came from a hand-written providers.opencode-go V2 block I added later, which v2 did reject — I've corrected that in the report).

    1. Credentials connected at the time: OpenCode Console OAuth (desktop /connect flow, account-level, device-flow credential in the credential database), plus a literal Go API key (sk- prefix) hanging in auth.json under the opencode entry. No opencode-go integration credential existed. After deleting the legacy block, the Console OAuth credential covers Go and everything works.

    One extra observation that may matter for your investigation: the overridden provider pinned npm: @ai-sdk/anthropic for all models, including deepseek-v4-flash, which per your endpoint table belongs on /chat/completions (@ai-sdk/openai-compatible). So the shadowing override also got the protocol/route wrong for DeepSeek — possibly compounding with the v2 @ai-sdk/anthropic + custom baseURL regression (#52502).

  3. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    Thanks for the details. I tried this on 2.0.26 with the CLI on Linux, with isolated config and data directories, using your provider.opencode-go block exactly but with baseURL pointed at a local server that logs incoming requests. I ran opencode run --standalone --model opencode-go/deepseek-v4-flash "say hi".

    What I saw:

    • The only warning was the ...deepseek-v4-flash.reasoning kind=unsupported one, as you said. The block is migrated and replaces the built-in Go provider.
    • Every request included the apiKey from your config. Requests to /zen/go/v1/messages sent it as x-api-key; one title request to /zen/go/v1/responses sent it as Authorization: Bearer. Adding an opencode entry to auth.json made no difference. OpenCode itself never reported Missing API key.
    • The real Go endpoint returns Invalid API key. for a wrong x-api-key, and Missing API key. only when x-api-key is absent (no key, or a Bearer header only). So the request that failed for you seems to have had no key in x-api-key, and I couldn't make that happen.

    It's expected that a provider.opencode-go block in your config replaces the built-in Go provider and its sign-in. Deleting it, as you did, is the right fix: in v2, signing in with OpenCode Console (/connect) also covers Go (see #52374). Your block also forced the Anthropic protocol for DeepSeek and Qwen, which isn't the route those models use.

    If you think OpenCode is still doing something wrong, these would help:

    • Does it happen with the CLI too (opencode run --standalone --print-logs --model opencode-go/deepseek-v4-flash "hi" with only the legacy block in place), or only in the desktop app?
    • The full error and log lines from that run (redact keys).
    • Whether the sk- key in the block was still valid. An old key from before the new Console would be rejected, but with Invalid API key, not Missing API key.

    Related: #52374 (Go and Console credentials), #52502 (@ai-sdk/anthropic with a custom baseURL), #50611 / #52243 / #51689 (desktop "Connect OpenCode Go" flow).

  4. yindongfang509 commented on Oct 10, 2026

    @yindongfang509
    Author

    Great analysis — I found the exact difference in our reproduction setups, and I think it explains everything.

    Your repro used deepseek-v4-flash, a model defined inside the legacy block. All of my failing sessions used models outside the block (from the built-in Go catalog). From the session database:

    • 2026-10-08 19:30 — opencode-go/step-5-preview-free
    • 2026-10-08 19:33 — opencode-go/longcat-2.5-preview-free
    • 2026-10-08 19:45 — opencode-go/step-5-preview-free
    • 2026-10-08 19:47 — opencode-go/deepseek-v4.1-flash (variant: high)

    All four are chat/completions-route models. Two distinct failures appeared with the legacy block in place: AI.Error: Missing API key on the main request, and AI.Error: Model does not support this protocol on others — consistent with these out-of-block models being served through the overridden provider (npm: @ai-sdk/anthropic, baseURL /zen/go/v1), i.e. the wrong protocol route plus no key on the wire.

    So the hypothesis to test: a model NOT defined in the legacy block's models map gets the provider's options (apiKey) dropped on the wire, while a model defined inside the block receives it (as you observed). If you can rerun with e.g. opencode run --standalone --model opencode-go/step-5-preview-free "hi" against your logging server, that should reproduce Missing API key.

    Other details from my side: the failures were on the desktop app, not CLI. The sk- key in the block was still valid on 10-10 — I verified it directly against the gateway (HTTP 200 on both /chat/completions and /messages), which matches your point that a wrong key yields Invalid API key, not Missing API key. Timeline: the last successful Go call was 2026-09-19; upgrade to 2.0.6 on 09-20; every Go call from 10-08 onward failed until the block was removed.

  5. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    Thanks for narrowing that down. I tested your idea on 2.0.26 (CLI, Linux) with your legacy block and the logging server, this time using catalog models that are not in the block: opencode-go/step-5-preview-free and opencode-go/deepseek-v4.1-flash.

    What I saw:

    • The key from the block was still on every request: x-api-key on /zen/go/v1/messages, and Authorization: Bearer on the title request to /zen/go/v1/responses. Models outside the block get the provider's apiKey the same way as models inside it, and OpenCode never reported Missing API key.
    • Those models were sent to the Anthropic /messages route, because the block sets npm: @ai-sdk/anthropic for the whole opencode-go provider. That most likely explains the Model does not support this protocol errors you saw. It's expected behaviour when a config block replaces the built-in provider, and removing the block (as you did) is the fix.

    So the CLI doesn't drop the key here. What I can't test is the Windows desktop app with your Console sign-in connected, which is where you saw it. If you can, it would help to run the same thing with the CLI on your machine, with only the legacy block in place:

    opencode run --standalone --print-logs --model opencode-go/step-5-preview-free "hi"
    

    and post the error and the log lines around it (with keys redacted). If the CLI works but the desktop app fails with the same config, that points to something specific to the desktop app. Related: #50611 / #52243 / #51689 (desktop Go connection flow) and #52374 (Go and Console credentials).

  6. yindongfang509 commented on Oct 10, 2026

    @yindongfang509
    Author

    Thanks for the thorough investigation — but I'd rather not re-reproduce this one. I just got the setup working again after the fix, and deliberately restoring the broken state on a working machine (even temporarily) isn't a trade-off I'm willing to make for a scenario that the CLI doesn't reproduce anyway.

    Where things stand, from my side, for the record:

    • The bug I hit was on the Windows desktop app with Console sign-in connected and the legacy provider.opencode-go block in place. Symptoms: AI.Error: Missing API key on the main request and AI.Error: Model does not support this protocol on others, for catalog models (step-5-preview-free, longcat-2.5-preview-free, deepseek-v4.1-flash) that are all chat/completions-route models.
    • Your testing showed the CLI passes the block's apiKey correctly for both in-block and out-of-block models, and that these out-of-block models get forced onto the Anthropic /messages route when the block replaces the built-in provider — which fully explains the Model does not support this protocol errors.
    • Deleting the block fixed everything, all 31 models working via Console OAuth. That part of the report stands.

    So the actionable takeaways that don't depend on my repro:

    1. A legacy V1 provider block silently replacing a built-in provider (with only an omitted unsupported legacy setting WARN for the stripped reasoning field) is a very quiet way to end up in a broken state. A louder diagnostic like "your provider.opencode-go block replaces the built-in OpenCode Go provider" would have saved users hours.
    2. That override also force-routes wrong-protocol models (chat/completions models onto /messages), and the resulting error message ("Model does not support this protocol") gives no hint that the cause is a leftover config block.
    3. The desktop-specific path I hit overlaps with the desktop Go connection issues you've already linked (desktop: "Connect OpenCode Go" runs Console sign-in instead of the Go API-key flow, then reports success and fails with "Insufficient account funds" #50611 / desktop: "Connect OpenCode Go" runs Console sign-in instead of the Go API-key flow, then reports success and fails with "Invalid credential" #52243 / OpenCode Go subscription not working in Desktop App: "Invalid credential" / "Insufficient account funds" and Go badges disappear #51689). I'd consider this issue covered by those + the "block replaces built-in provider" behaviour, and it's fine to close it as not-reproduced-on-CLI if that's your call.

    Thanks again for the detailed back-and-forth — it was genuinely useful in narrowing this down.

  7. opencode-agent commented on Oct 10, 2026

    @opencode-agent
    Contributor

    Thanks for the summary, and for all the back-and-forth. It narrowed this down a lot, and it's completely fair not to put the broken setup back.

    To restate what's left: you'd like OpenCode to warn clearly when a leftover provider.opencode-go block (a legacy one or a providers entry) replaces the built-in OpenCode Go provider and its sign-in. Today the only startup message is about the dropped reasoning field. It would also help if the resulting errors, like Model does not support this protocol when the block's @ai-sdk/anthropic package sends chat/completions models to /messages, pointed at the config override as the likely cause. Replacing the built-in provider is intended, but you're right that it happens very quietly, so I've recorded this as a request for clearer diagnostics. Maintainers will consider it.

    For the record, I couldn't reproduce the Missing API key itself with the CLI on 2.0.26: models inside and outside the block both received the key from your config. The desktop app with a Console sign-in is the part that's still untested.

    Related:

  8. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Closing this as not planned. As the discussion above confirmed, the config still had a V1 provider.opencode-go block. V2 migrated that block as a custom provider, and it replaced the built-in OpenCode Go provider, including its sign-in, its credentials, and the protocol each model uses. That custom block is why requests failed, and deleting it was the correct fix.

    In V2, signing in to OpenCode Console with /connect also covers Go, so you don't need an opencode-go entry in your config. Add a provider block only if you deliberately want to override the built-in one.

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