siyuan-note/siyuan · error
OAuth flow is missing or expired
Error message
OAuth flow is missing or expired
What it means
CompleteMCPOAuth looks up the in-memory OAuth flow registry by flowID and completes the authorization-code exchange when the browser callback arrives. This error means no flow with that ID exists or the flow's expiry time has passed; the entry is deleted and the callback is rejected. Flows are ephemeral in-memory state lost on kernel restart and expiring on a timer.
Solutions
- Restart the OAuth authorization flow (start MCP OAuth again) to get a fresh flowID and redirect URL.
- Complete the callback promptly; do not leave the login page open past the expiry window.
- After a kernel restart, abandon in-flight browser flows and start over.
- Ensure the callback URL is delivered exactly once; ignore duplicates after the flow completes.
- Do not manually construct or reuse flowIDs; only use the ID returned when the flow was started.
Defensive patterns
Strategy: try-catch
Try / catch
if err := CompleteMCPOAuth(flowID, code, state, callbackErr, issuer); err != nil {
if strings.Contains(err.Error(), "missing or expired") {
restartOAuthFlow(server) // fresh flowID and state
}
} Prevention
- Complete browser login promptly; flows expire on a timer
- Never reuse a flowID or replay an old callback URL
- Start a new flow after kernel restarts; in-memory flows are lost
- Deliver the callback exactly once and ignore later duplicates
When it happens
Trigger: CompleteMCPOAuth (invoked by the OAuth callback HTTP route) with a flowID that was never started, was already consumed (deleted after completion), or whose flow.Expires timestamp is in the past. Also after a kernel restart, since oauthFlows is memory-only.
Common situations: User takes too long in the browser to finish login and clicks authorize after expiry; user pastes an old callback URL; kernel restarted between starting the flow and the callback; duplicated callback request (e.g. browser prefetch) after the flow completed.
Related errors
- configuration is not initialized
- discover OAuth authorization server
- exchange OAuth authorization code
- mcp oauth authorization required
- OAuth authorization failed
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/07debdf0733bdfad.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/mcp/client/oauth.go:592
}
return base64.RawURLEncoding.EncodeToString(data), nil
}
func removeOAuthFlow(flowID string, flow *oauthFlow) {
oauthFlows.Lock()
if oauthFlows.items[flowID] == flow {
delete(oauthFlows.items, flowID)
}
oauthFlows.Unlock()
}
func CompleteMCPOAuth(flowID, code, state, callbackError, issuer string) error {
oauthFlows.Lock()
flow := oauthFlows.items[flowID]
if flow == nil || time.Now().After(flow.Expires) {
delete(oauthFlows.items, flowID)
oauthFlows.Unlock()
return fmt.Errorf("OAuth flow is missing or expired")
}
if state != flow.State {
oauthFlows.Unlock()
return fmt.Errorf("OAuth state mismatch")
}
if issuer != "" && issuer != flow.Issuer {
oauthFlows.Unlock()
return fmt.Errorf("OAuth issuer mismatch")
}
delete(oauthFlows.items, flowID)
oauthFlows.Unlock()
select {
case flow.Result <- oauthCallbackResult{Code: code, State: state, Error: callbackError}:
return nil
default:
return fmt.Errorf("OAuth callback was already handled")
}
}View on GitHub (pinned to 9f775e8a12)