siyuan-note/siyuan · warning

OAuth authorization timed out

Error message

OAuth authorization timed out

What it means

Returned at oauth.go:382-383 when the 5-minute oauthAuthorizationTimeout timer fires before the OAuth callback channel receives a result. The flow waits on a select over flow.Result, ctx.Done(), and the timer; the user must complete browser consent and the callback must reach /api/ai/mcp/oauth/callback/<flowID> within the window.

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 251596fc0d)

Solutions

  1. Re-initiate the authorization promptly and complete browser consent within the 5-minute window.
  2. Confirm the kernel is reachable at http://127.0.0.1:<util.ServerPort> and that the AS redirects back to the exact callback URL shown in the authorization URL.
  3. Disable host firewall/AV blocking of the loopback callback port.
  4. If SSO/MFA routinely exceeds 5 minutes, request a longer oauthAuthorizationTimeout (source change) or pre-authenticate the AS session.

Example fix

// before: user starts auth then leaves; flow times out
// after: re-trigger Authorize and finish the browser consent immediately,
// ensuring the redirect to http://127.0.0.1:<port>/api/ai/mcp/oauth/callback/<id> is allowed
Defensive patterns

Strategy: retry

Try / catch

// Interactive Authorize caller — treat timeout as retriable user action.
err := handler.Authorize(ctx, req, resp)
if err != nil && strings.Contains(err.Error(), "OAuth authorization timed out") {
    setMCPRuntimeStateForContext(ctx, serverID, "authorization_required", 0, "authorization timed out; please retry", "")
    return // allow the user to re-trigger
}

Prevention

When it happens

Trigger: Interactive Authorize opened the authorization URL but CompleteMCPOAuth was never called (user did not finish consent), was called after the flow's Expires time, or the callback HTTP request never reached the kernel (firewall, wrong port, browser blocked the redirect to 127.0.0.1).

Common situations: User walked away from the browser; browser failed to open or open the wrong URL; the 127.0.0.1 callback port blocked by host firewall/AV; long AS login (MFA, SSO) exceeding 5 minutes; process restarted clearing the in-memory oauthFlows map before callback arrived.

Understand the failure class

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/96aa42925a4019f2. Report an issue: GitHub.