{"record":{"id":"e8dcc29694d19712","repo":"github/github-mcp-server","slug":"failed-to-fetch-raw-content-s","errorCode":null,"errorMessage":"failed to fetch raw content: %s","messagePattern":"failed to fetch raw content: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/github/repository_resource.go","lineNumber":254,"sourceCode":"\t\t\t\t}\n\n\t\t\t\treturn &mcp.ReadResourceResult{\n\t\t\t\t\tContents: []*mcp.ResourceContents{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tURI:      request.Params.URI,\n\t\t\t\t\t\t\tMIMEType: mimeType,\n\t\t\t\t\t\t\tBlob:     buf.Bytes(),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}, nil\n\t\t\t}\n\t\tcase resp.StatusCode != http.StatusNotFound:\n\t\t\t// If we got a response but it is not 200 OK, we return an error\n\t\t\tbody, err := io.ReadAll(resp.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to read response body: %w\", err)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"failed to fetch raw content: %s\", string(body))\n\t\tdefault:\n\t\t\t// This should be unreachable because GetContents should return an error if neither file nor directory content is found.\n\t\t\treturn nil, errors.New(\"404 Not Found\")\n\t\t}\n\t}\n}\n\n// expandRepoResourceURI builds a resource URI using the appropriate URI template\n// based on the provided parameters (sha, ref, or default).\nfunc expandRepoResourceURI(owner, repo, sha, ref string, pathParts []string) (string, error) {\n\tbaseValues := uritemplate.Values{\n\t\t\"owner\": uritemplate.String(owner),\n\t\t\"repo\":  uritemplate.String(repo),\n\t\t\"path\":  uritemplate.List(pathParts...),\n\t}\n\n\tswitch {\n\tcase sha != \"\":","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/pkg/github/repository_resource.go#L236-L272","documentation":"The raw content endpoint for a repo:// resource returned a status other than 200 or 404, and this error embeds the raw response body verbatim via %s. Typical bodies are GitHub's error documents: 401 'Bad credentials' (expired/revoked token), 403 rate limit or blocked repo, or 5xx from the raw CDN. The message omits the HTTP status code, so the cause must be inferred from the body text.","triggerScenarios":"Expired or revoked PAT on raw.githubusercontent.com; secondary rate limit (403 'You have exceeded a secondary rate limit') from aggressive raw fetches; private repo the token cannot read; 5xx incident on the raw CDN; wrong raw host for GH_HOST/GHE configurations.","commonSituations":"Long-running servers whose token expired mid-session; CI jobs fetching many raw files and tripping abuse detection; misconfigured GH_HOST so raw requests go to a host that rejects them.","solutions":["Read the embedded body: 'Bad credentials' means rotate/refresh the token; 'rate limit' means back off and respect Retry-After","Verify the token against raw content directly: curl -H \"Authorization: Bearer $TOKEN\" https://raw.githubusercontent.com/owner/repo/main/README.md","For 403 secondary rate limits, wait and retry with exponential backoff","Improve the handler to prefix resp.Status so triage does not depend on parsing the body"],"exampleFix":"// before\nreturn nil, fmt.Errorf(\"failed to fetch raw content: %s\", string(body))\n// after - keep the status code and trim the body\nreturn nil, fmt.Errorf(\"failed to fetch raw content: %s: %s\", resp.Status, bytes.TrimSpace(body))","handlingStrategy":"try-catch","validationCode":"// Fail fast before bulk raw reads: verify the token and rate-limit headroom\nrl, resp, err := client.RateLimit.Get(ctx)\nif err != nil || resp.StatusCode != http.StatusOK {\n    return fmt.Errorf(\"token or API unusable, aborting raw reads: %v\", err)\n}\nif rl.GetCore().Remaining == 0 {\n    return fmt.Errorf(\"rate limit exhausted until %v\", rl.GetCore().Reset.Time)\n}","typeGuard":"func classifyRawContentError(msg string) string {\n\tswitch {\n\tcase strings.Contains(msg, \"Bad credentials\"):\n\t\treturn \"invalid-token\"\n\tcase strings.Contains(msg, \"rate limit\"), strings.Contains(msg, \"secondary rate limit\"):\n\t\treturn \"rate-limited\"\n\tcase strings.Contains(msg, \"404\"):\n\t\treturn \"not-found\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}","tryCatchPattern":"result, err := readResource(ctx, uri)\nif err != nil && strings.HasPrefix(err.Error(), \"failed to fetch raw content\") {\n    switch classifyRawContentError(err.Error()) {\n    case \"invalid-token\":\n        return errors.New(\"raw content token expired or revoked; refresh it\")\n    case \"rate-limited\":\n        time.Sleep(time.Until(resetAt)) // then retry\n    default:\n        return err\n    }\n}","preventionTips":["Rotate tokens before expiry so raw fetches never send dead credentials","Respect Retry-After and x-ratelimit-reset on 403/429 before more raw reads","Keep GH_HOST/raw host configuration consistent with the token's instance (github.com vs GHE)"],"tags":["go","mcp","github-api","auth","rate-limit","resource","http"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}