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
fix(mcp): silent fallback when OAuth callback port is in use surfaces as misleading CSRF error #30888
When 127.0.0.1:19876 (the hardcoded OAuth callback port) is already in use, McpOAuthCallback.ensureRunning() silently returns without starting our callback server:
The OAuth flow then opens the browser anyway and waits for a callback. The browser hits the other process listening on 19876, which either 404s or — if it's another opencode instance — sees a state it doesn't know about and shows:
Authorization Failed
Invalid or expired state parameter - potential CSRF attack
That message points the user at a CSRF / cookie / browser issue. The real cause is "the callback port is occupied".
How I hit it
Shared dev host with two users. The other user's long-running bun dev serve had grabbed 19876 days ago. My opencode mcp auth Notion failed every time with the CSRF error. Took an hour to trace.
Why it matters
The fields to fix it (oauth.callbackPort, oauth.redirectUri) already exist on McpOAuthConfig and are wired through ensureRunning. The user just has no way to know they need to set them.
Anyone on a shared host, dev container, GitHub Codespaces, or a machine where 19876 happens to be taken (it's not in IANA's well-known range but it's not reserved either) gets the same misleading error.
Proposed fix
Throw a clear error from ensureRunning when the port is busy, naming the config knob:
OAuth callback port 19876 is already in use. Set "oauth.callbackPort"
(or "oauth.redirectUri") on the MCP server entry in your opencode config
to use a different port.
This changes one branch from "silent return" to "throw", so it's a small behaviour change. The only thing it breaks is the (apparently undocumented) ability for two opencode instances on the same machine to share one callback server — which is racy anyway (states are kept in the first instance's memory, so the second's auth would silently fail).
This issue might be a duplicate of existing issues. Please check:
[Bug]: OAuth callback server (port 19876) not stopped after authentication completes #23563: OAuth callback server (port 19876) not stopped after authentication — describes the same root cause where port 19876 is held by another opencode instance, causing the next OAuth flow to see an unknown state and show the same "Invalid or expired state parameter - potential CSRF attack" error
The core difference is your report focuses on the case where the occupying process is not another opencode instance (e.g. a different user's process), while those issues focus on multiple opencode instances. Both cases stem from the same silent-return behavior in ensureRunning() and the same misleading CSRF error. It may be worth commenting on one of those existing issues rather than opening a new one, or at least linking them.
This is still an issue. I'm using Openchamber. All OAuth attempts use 127.0.0.1:19876. If another OpenCode/OpenChamber process owns that port, the new process silently continues but stores its OAuth state in a separate in-memory map. The callback then reaches the wrong process.
Activity
github-actions commentedon Jun 5, 2026
This issue might be a duplicate of existing issues. Please check:
The core difference is your report focuses on the case where the occupying process is not another opencode instance (e.g. a different user's process), while those issues focus on multiple opencode instances. Both cases stem from the same silent-return behavior in
ensureRunning()and the same misleading CSRF error. It may be worth commenting on one of those existing issues rather than opening a new one, or at least linking them.github-actions commentedon Aug 5, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.
mattv8 commentedon Aug 11, 2026
This is still an issue. I'm using Openchamber. All OAuth attempts use 127.0.0.1:19876. If another OpenCode/OpenChamber process owns that port, the new process silently continues but stores its OAuth state in a separate in-memory map. The callback then reaches the wrong process.