siyuan-note/siyuan · error

load GitHub user failed: %w

Error message

load GitHub user failed: %w

What it means

For GitHub providers, Exchange() calls exchangeGitHubClaims, which uses the exchanged OAuth token to call GET https://api.github.com/user. Any failure of that HTTP call (transport error, non-2xx status from getGitHubJSON, decode error) is wrapped with this message, so login succeeds at the token step but fails when loading the user profile.

Source

Thrown at kernel/model/oidc_provider/provider.go:162

			},
			RedirectURL: redirectURL,
			Scopes:      scopes,
		},
	}
}

func isDefaultOIDCScopes(scopes []string) bool {
	if len(scopes) != 3 {
		return false
	}
	return contains(scopes, oidc.ScopeOpenID) && contains(scopes, "profile") && contains(scopes, "email")
}

func exchangeGitHubClaims(ctx context.Context, token *oauth2.Token) (map[string]any, error) {
	client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token))
	user := map[string]any{}
	if err := getGitHubJSON(ctx, client, "https://api.github.com/user", &user); err != nil {
		return nil, fmt.Errorf("load GitHub user failed: %w", err)
	}
	delete(user, "email")
	if id, ok := user["id"]; ok {
		user["sub"] = fmt.Sprint(id)
	}
	emails := []struct {
		Email    string `json:"email"`
		Primary  bool   `json:"primary"`
		Verified bool   `json:"verified"`
	}{}
	if err := getGitHubJSON(ctx, client, "https://api.github.com/user/emails", &emails); err == nil {
		all := make([]string, 0, len(emails))
		for _, email := range emails {
			if !email.Verified {
				continue
			}
			all = append(all, email.Email)
			if email.Primary {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Confirm the kernel host can reach https://api.github.com/user (curl it; check proxy settings)
  2. Ensure the OAuth app scopes include read:user and user:email, or reset scopes to the defaults in SiYuan's GitHub OIDC configuration
  3. Check rate-limit status (X-RateLimit-Remaining) and retry after the reset window if the status was 403 with rate-limit headers
  4. Retry the login; if GitHub is having an incident, wait and try again
  5. Regenerate the flow if the access token was revoked between exchange and profile load
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Get("https://api.github.com/rate_limit")
if err != nil {
    return errors.New("api.github.com unreachable from this host")
}
resp.Body.Close()

Try / catch

user, err := provider.Exchange(ctx, code, verifier, nonce)
if err != nil && strings.Contains(err.Error(), "load GitHub user failed") {
    // transient GitHub/network issue: allow one bounded retry of the whole flow
    return retryLoginFlow(maxAttempts=2)
}

Prevention

When it happens

Trigger: Provider.Exchange with kind=github where the api.github.com/user request fails: network outage, proxy blocking api.github.com, token lacking read:user scope (403/404), token revoked mid-flow, or GitHub rate limiting (403 with rate-limit headers).

Common situations: Corporate proxy/firewall blocking api.github.com; GitHub OAuth app scopes customized to drop read:user; hitting GitHub's per-IP rate limit on shared egress (CI, NAT); transient GitHub incidents.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/9e7a7b7e97a04b29. Report an issue: GitHub.