siyuan-note/siyuan · error
OAuth token endpoint returned no access token
Error message
OAuth token endpoint returned no access token
What it means
The token endpoint returned HTTP 2xx with JSON that decoded successfully, but the access_token field was empty or missing. A protocol-compliant token response must contain a non-empty access_token; an empty one makes the result unusable, so oauthTokenRequest rejects it.
Solutions
- Verify the token endpoint URL is the actual token_endpoint from the server's metadata, not another API route.
- Dump the 200 response body to inspect what the server actually returned.
- Confirm the grant is permitted: some IdPs return 200 with an empty token when refresh tokens are revoked — re-authenticate with a fresh authorize flow.
- Upgrade or fix the IdP if it violates RFC 6749 by omitting access_token on success.
- Check for proxies rewriting the response body.
Defensive patterns
Strategy: validation
Validate before calling
var body map[string]any
json.NewDecoder(resp.Body).Decode(&body)
if at, _ := body["access_token"].(string); at == "" {
// server returns 200 without access_token; it is non-conformant or the endpoint is wrong
} Try / catch
if _, _, err := oauthTokenRequest(ctx, client, credential, values); err != nil {
if strings.Contains(err.Error(), "no access token") {
log.Error("non-conformant token response; re-authenticating")
startFreshAuthorizeFlow(server)
}
} Prevention
- Confirm the endpoint is the real token_endpoint from metadata, not another API
- Log token responses during setup to catch non-conformant IdPs early
- Re-authenticate when refresh tokens may have been revoked server-side
- Prefer IdPs certified against RFC 6749
When it happens
Trigger: Called from refreshOAuthCredential when the server responds 200 with a JSON body lacking access_token — e.g. a non-standard success body, a server bug, an HTML 'success' page that coincidentally parses differently, or a response containing only refresh-related fields.
Common situations: Non-conformant IdP implementations; middleware returning 200 with an error-shaped body; grant type silently ignored (server returns empty token instead of an error); misconfigured token endpoint pointing to a non-token API that returns 200 JSON.
Related errors
- OAuth token endpoint returned
- configuration is not initialized
- discover OAuth authorization server
- exchange OAuth authorization code
- load GitHub user failed
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/c0d68be921a16b77.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/mcp/client/oauth.go:687
}
defer resp.Body.Close()
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
if err != nil {
return nil, nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
tokenErr := &oauthTokenError{}
if json.Unmarshal(body, tokenErr) != nil || tokenErr.Code == "" {
return nil, nil, fmt.Errorf("OAuth token endpoint returned %s", resp.Status)
}
return nil, tokenErr, tokenErr
}
result := &oauthTokenResponse{}
if err = json.Unmarshal(body, result); err != nil {
return nil, nil, err
}
if result.AccessToken == "" {
return nil, nil, fmt.Errorf("OAuth token endpoint returned no access token")
}
if result.TokenType != "" && !strings.EqualFold(result.TokenType, "Bearer") {
return nil, nil, fmt.Errorf("OAuth token endpoint returned unsupported token type %q", result.TokenType)
}
return result, nil, nil
}
func applyOAuthClientAuthentication(values url.Values, req *http.Request, credential oauthCredential) {
switch credential.TokenAuthMethod {
case "client_secret_basic":
if req != nil {
req.SetBasicAuth(url.QueryEscape(credential.ClientID), url.QueryEscape(credential.ClientSecret))
}
default:
if values != nil {
values.Set("client_id", credential.ClientID)
if credential.TokenAuthMethod == "client_secret_post" && credential.ClientSecret != "" {
values.Set("client_secret", credential.ClientSecret)View on GitHub (pinned to 9f775e8a12)