anomalyco / anomalyco/opencode
Concurrent OAuth refresh from multiple processes kills rotating refresh-token series
@jlongster is already working on this.
Since Sep 17, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
What happens
When several opencode processes share one mcp-auth.json, each process
refreshes OAuth tokens independently on 401. There is no cross-process
single-flight for the refresh transaction (EffectFlock in
packages/opencode/src/mcp/auth.ts only serializes file read/write, not the
refresh itself).
Against an IdP that rotates refresh tokens and revokes the whole family on
reuse, a single overlap is fatal:
- Process A and B both read RT1 from the shared file, both POST it to /token.
- The winner gets RT2. The loser gets
invalid_grant(reuse detected) and the
IdP kills the entire series. - The SDK (
auth()in@modelcontextprotocol/sdk@1.29.0src/client/auth.ts) then callsinvalidateCredentials('tokens'), wiping stored tokens. The server staysneeds_auth` until the user re-authenticates
manually.
With 7 processes and 300s access tokens, refreshes happen every few minutes
around the clock, so one overlap per day is enough to break the setup
permanently. Observed: 27h of continuous needs_auth, mcp-auth.json left
with only clientInfo + codeVerifier + oauthState.
Related: no retry
Failed tool calls are not retried (withClient in
packages/opencode/src/mcp/index.ts falls back to undefined; connect maps
UnauthorizedError to needs_auth + toast). So the losing call is silently
dropped even when a retry a second later would succeed.
Ask
- Single-flight refresh across processes sharing one auth file (or at least
re-read + retry once after a refresh race), and/or - Don't wipe stored tokens on a transient refresh failure; keep them so the
next attempt can use the winner's rotated pair.
Env
opencode 1.18.30, @modelcontextprotocol/sdk 1.29.0, Windows, remote MCP
(StreamableHTTP) + self-hosted OAuth IdP with rotation + reuse detection.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.