siyuan-note/siyuan · error
OAuth authorization timed out
Error message
OAuth authorization timed out
What it means
Authorize waits on a channel for the browser OAuth callback, with a fixed timeout (oauthAuthorizationTimeout). If neither the callback arrives nor the context is cancelled before the timer fires, the flow is abandoned and this error is returned. The pending flow is removed via the deferred removeOAuthFlow.
Solutions
- Retry authorization and complete the consent flow promptly in the browser
- Ensure the callback URL http://127.0.0.1:<kernel-port>/... is reachable from the browser used for login (local vs remote/SSH setups)
- Check that the kernel's callback endpoint /api/ai/mcp/oauth/callback/<flowID> is not blocked by a proxy or firewall
- If the IdP is very slow, increase oauthAuthorizationTimeout upstream
Defensive patterns
Strategy: retry
Validate before calling
// Check reachability of the local callback endpoint before starting
resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%s/api/ai/mcp/oauth/ping", util.ServerPort))
if err != nil { /* callback unreachable: fix port/proxy first */ } Try / catch
if err := h.Authorize(ctx, true); err != nil {
if strings.Contains(err.Error(), "timed out") {
// retry the flow; ensure the user completes the browser step promptly
}
} Prevention
- Open the authorization URL immediately and complete consent in one sitting
- Use a browser that can reach 127.0.0.1:<kernel-port> (local browser, not one on another machine)
- Avoid suspending the machine mid-flow; disable aggressive popup blockers
- On remote/headless setups, tunnel the localhost callback port
When it happens
Trigger: Authorize() registered an oauthFlow and printed an authorization URL; the user never completed (or never opened) the consent page in the browser within oauthAuthorizationTimeout, so the timer.C branch fires.
Common situations: User missed or ignored the browser popup; machine suspended during the flow; slow IdP consent page; headless/remote server where the callback URL (127.0.0.1:<port>) is unreachable from the user's browser.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- configuration is not initialized
- connect
- discover OAuth authorization server
- exchange OAuth authorization code
- mcp oauth authorization required
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/96aa42925a4019f2.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/mcp/client/oauth.go:383
Issuer: asm.Issuer,
Result: make(chan oauthCallbackResult, 1),
Expires: time.Now().Add(oauthAuthorizationTimeout),
}
oauthFlows.Lock()
oauthFlows.items[flowID] = flow
oauthFlows.Unlock()
defer removeOAuthFlow(flowID, flow)
setMCPRuntimeStateForContext(ctx, h.server.ID, "authorizing", 0, "", authorizationURL)
var callback oauthCallbackResult
timer := time.NewTimer(oauthAuthorizationTimeout)
defer timer.Stop()
select {
case callback = <-flow.Result:
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
return fmt.Errorf("OAuth authorization timed out")
}
if callback.Error != "" {
return fmt.Errorf("OAuth authorization failed: %s", callback.Error)
}
if callback.State != state {
return fmt.Errorf("OAuth state mismatch")
}
if callback.Code == "" {
return fmt.Errorf("OAuth callback did not include an authorization code")
}
exchangeCtx := context.WithValue(ctx, oauth2.HTTPClient, h.client)
token, err := config.Exchange(exchangeCtx, callback.Code,
oauth2.VerifierOption(verifier),
oauth2.SetAuthURLParam("resource", prm.Resource))
if err != nil {
return fmt.Errorf("exchange OAuth authorization code: %w", err)
}View on GitHub (pinned to 9f775e8a12)