You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Provider API key cannot be rotated in place; Edit silently keeps the expired key #58043
Provider API key cannot be rotated in place; Edit silently keeps the expired key
Version: 2.0.22 (also present in current v2 HEAD source) Area: dashboard provider settings + credential.update API
What happens
A provider API key expires (provider returns 401 api_key_expired on model queries).
The dashboard provider card shows unhealthy + "Could not query models".
The user opens Edit, enters the new key, and saves. No error is shown.
Nothing changes: the stored credential keeps the old key, the provider stays unhealthy, and the model inventory stays stale.
Root cause
Key rotation through Edit is structurally impossible in the current contract:
Client sends PATCH /api/credential/{id} with { label } only.
Server handler (packages/server/src/handlers/credential.ts, credential.update) applies only { label: ctx.payload.label }; any value sent is silently dropped (verified live: PATCH with a value returns 204 and leaves the stored key untouched).
API schema (packages/protocol/src/groups/credential.ts) defines the update payload as Schema.Struct({ label: Schema.String }), described literally as "Update a stored credential label."
So there is no in-place key rotation path at all. The only working path is Remove + re-add (create accepts the full value — verified working).
Why this matters
A user doing the obvious thing (Edit → paste new key → save) gets silent success with zero effect, while the provider remains broken. There is no error, no hint that Edit cannot touch secrets.
The health check ("Could not query models") swallows the provider's actual error (api_key_expired), so nothing points at the expired key.
Suggested fix
Either:
Accept an optional value on credential.update (re-verify it against the provider before persisting), and have the dashboard Edit dialog send it; or
If in-place rotation is intentionally unsupported, make the Edit dialog explicit (label-only) and add a first-class "Replace key" action (remove + create in one flow), and surface the provider's auth error in the health message instead of the generic "Could not query models".
Repro (local server, no secrets needed)
Create a credential via POST /api/credential (works, persists).
PATCH /api/credential/{id} with {"label": "...", "value": {...new key...}} → 204.
GET /api/credential → the old key is still stored. No error at any step.
You're asking for a way to replace a stored provider credential's API key in place, rather than removing it and adding it again, and for the real auth error (e.g. api_key_expired) to be shown when model listing fails.
As it stands, credential.update (PATCH /api/credential/{id}) is intentionally label-only. Its schema is { label: string } and it's described as "Update a stored credential label." Extra fields like value are dropped during payload decoding, which is why you get a 204 and the old key stays. So this works as designed today, but the silent no-op is a fair point, and in-place rotation would be a reasonable addition. I've marked this as a feature request for maintainers to consider.
One note: I couldn't find the provider card with an Edit dialog or the "Could not query models" message in this repository. OpenCode's own app only offers connect, switch account and disconnect for providers. If you're using a third-party or custom dashboard, its Edit dialog is the part that implies the key can be changed. Until then, removing the credential and creating it again (as you found) is the way to rotate a key.
The update endpoint dropped any value payload — the core Credential.update already supported updating it, only the protocol schema and handler didn't pass it through. I verified the silent-drop repro against a local server and opened #58066 to accept an optional value on credential.update.
Activity
opencode-agent commentedon Oct 22, 2026
Thanks for the detailed write-up and repro!
You're asking for a way to replace a stored provider credential's API key in place, rather than removing it and adding it again, and for the real auth error (e.g.
api_key_expired) to be shown when model listing fails.As it stands,
credential.update(PATCH /api/credential/{id}) is intentionally label-only. Its schema is{ label: string }and it's described as "Update a stored credential label." Extra fields likevalueare dropped during payload decoding, which is why you get a 204 and the old key stays. So this works as designed today, but the silent no-op is a fair point, and in-place rotation would be a reasonable addition. I've marked this as a feature request for maintainers to consider.One note: I couldn't find the provider card with an Edit dialog or the "Could not query models" message in this repository. OpenCode's own app only offers connect, switch account and disconnect for providers. If you're using a third-party or custom dashboard, its Edit dialog is the part that implies the key can be changed. Until then, removing the credential and creating it again (as you found) is the way to rotate a key.
Related issues:
Bensonluo commentedon Oct 22, 2026
The update endpoint dropped any
valuepayload — the coreCredential.updatealready supported updating it, only the protocol schema and handler didn't pass it through. I verified the silent-drop repro against a local server and opened #58066 to accept an optionalvalueoncredential.update.