{"record":{"id":"c997046c84d72a73","repo":"siyuan-note/siyuan","slug":"github-api-returned-status-d","errorCode":null,"errorMessage":"GitHub API returned status %d","messagePattern":"GitHub API returned status (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc_provider/provider.go","lineNumber":204,"sourceCode":"\t}\n\treturn user, nil\n}\n\nfunc getGitHubJSON(ctx context.Context, client *http.Client, endpoint string, target any) error {\n\trequest, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\trequest.Header.Set(\"Accept\", \"application/vnd.github+json\")\n\trequest.Header.Set(\"X-GitHub-Api-Version\", \"2022-11-28\")\n\trequest.Header.Set(\"User-Agent\", \"SiYuan\")\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer response.Body.Close()\n\tif response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {\n\t\treturn fmt.Errorf(\"GitHub API returned status %d\", response.StatusCode)\n\t}\n\tdecoder := json.NewDecoder(io.LimitReader(response.Body, 1024*1024))\n\tdecoder.UseNumber()\n\treturn decoder.Decode(target)\n}\n\nfunc contains(values []string, target string) bool {\n\tfor _, value := range values {\n\t\tif value == target {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n","sourceCodeStart":186,"sourceCodeEnd":219,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/oidc_provider/provider.go#L186-L219","documentation":"getGitHubJSON is the shared HTTP helper for GitHub API calls in the OIDC GitHub flow. It rejects any response whose status code is outside 2xx with this formatted error carrying the numeric status. Callers wrap it (e.g. load GitHub user failed) but the status number here identifies the actual upstream rejection.","triggerScenarios":"Any GitHub API call in exchangeGitHubClaims (api.github.com/user or api.github.com/user/emails) returning 401 (bad token), 403 (forbidden scopes or rate limit), 404 (resource/app misconfigured), or 5xx (GitHub outage).","commonSituations":"Access token expired or revoked (401); OAuth app scopes too narrow (403); shared-IP rate limiting (403); GitHub brownouts/deprecations; corporate egress proxy returning 407/502.","solutions":["Map the status: 401 - re-authenticate (token invalid); 403 - check scopes or rate limit headers; 404 - verify app configuration; 5xx - retry later","Ensure requested scopes include read:user and user:email for the /user and /user/emails endpoints","If rate limited, wait until the X-RateLimit-Reset time or reduce request volume from that egress IP","Verify no proxy is rewriting the response (check for HTML error pages via curl from the kernel host)","Retry the login flow once GitHub status is healthy (check www.githubstatus.com)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := getGitHubJSON(ctx, client, url, &target); err != nil {\n    var se interface{ Error() string }\n    if errors.As(err, &se) && strings.Contains(se.Error(), \"status 403\") {\n        // inspect X-RateLimit-Reset and back off before retrying\n    }\n    return err\n}","preventionTips":["Check the numeric status in the message and act accordingly (401 re-auth, 403 scopes/rate-limit, 5xx retry)","Verify scopes cover each endpoint called (/user/emails needs user:email)","Avoid hammering the GitHub API from shared IPs; back off on 403","Test endpoints with curl using the same Accept and API-version headers"],"tags":["github","http","api","status-code"],"backgroundTag":"http-non-2xx-response","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}