{"record":{"id":"d4b5cac30d2d9512","repo":"AlistGo/alist","slug":"res-status-d4b5ca","errorCode":null,"errorMessage":"res.Status()","messagePattern":"res\\.Status\\(\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/github/util.go","lineNumber":44,"sourceCode":"\tTargetPath string\n}\n\nfunc getMessage(tmpl *template.Template, vars *MessageTemplateVars, defaultOpStr string) (string, error) {\n\tsb := strings.Builder{}\n\tif err := tmpl.Execute(&sb, vars); err != nil {\n\t\treturn fmt.Sprintf(\"%s %s %s\", vars.UserName, defaultOpStr, vars.ObjPath), err\n\t}\n\treturn sb.String(), nil\n}\n\nfunc calculateBase64Length(inputLength int64) int64 {\n\treturn 4 * ((inputLength + 2) / 3)\n}\n\nfunc toErr(res *resty.Response) error {\n\tvar errMsg ErrResp\n\tif err := utils.Json.Unmarshal(res.Body(), &errMsg); err != nil {\n\t\treturn errors.New(res.Status())\n\t} else {\n\t\treturn fmt.Errorf(\"%s: %s\", res.Status(), errMsg.Message)\n\t}\n}\n\n// Example input:\n// a = /aaa/bbb/ccc\n// b = /aaa/b11/ddd/ccc\n//\n// Output:\n// ancestor = /aaa\n// aChildName = bbb\n// bChildName = b11\n// aRest = bbb/ccc\n// bRest = b11/ddd/ccc\nfunc getPathCommonAncestor(a, b string) (ancestor, aChildName, bChildName, aRest, bRest string) {\n\ta = utils.FixAndCleanPath(a)\n\tb = utils.FixAndCleanPath(b)","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/github/util.go#L26-L62","documentation":"toErr is the generic resty-response-to-error converter for the GitHub driver's non-2xx API replies. It tries to unmarshal the body into ErrResp to append GitHub's 'message' field; when the body is not valid JSON (empty body, HTML error page, plain-text proxy response) it degrades to returning errors.New(res.Status()), i.e. the bare status line such as '403 Forbidden'.","triggerScenarios":"Any GitHub REST call made through resty that returns an error status with a non-JSON body: 502/504 HTML from a gateway, empty bodies on 403 secondary-rate-limit, corporate proxy interception, or GitHub outage pages.","commonSituations":"Primary or secondary rate limits (GitHub sometimes omits the JSON body), GitHub incidents, requests routed through a reverse proxy that rewrites error responses.","solutions":["Inspect the status: 403 -> check X-RateLimit-* headers and honor Retry-After; 5xx -> retry with backoff later","Confirm the token is valid and has the needed scopes (401/403)","If behind a proxy, ensure it forwards GitHub responses untouched","Retry the operation after the wait window; the failure is usually transient"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check rate limits before heavy operations\nres, _ := client.R().SetContext(ctx).Get(\"https://api.github.com/rate_limit\")\nif res.StatusCode() == 200 {\n\tvar rl struct{ Rate struct{ Remaining int; Reset int64 } }\n\t_ = utils.Json.Unmarshal(res.Body(), &rl)\n\tif rl.Rate.Remaining < 10 { return fmt.Errorf(\"rate limit nearly exhausted; resets at %d\", rl.Rate.Reset) }\n}","typeGuard":null,"tryCatchPattern":"err := someGithubCall()\nif err != nil {\n\tmsg := err.Error()\n\tswitch {\n\tcase strings.Contains(msg, \"403\"): return fmt.Errorf(\"rate-limited or forbidden: %w\", err)\n\tcase strings.Contains(msg, \"5\"): time.Sleep(30 * time.Second); return someGithubCall()\n\t}\n\treturn err\n}","preventionTips":["Authenticate all API traffic — authorized limits are 5-30x higher","Cache tree/list responses to reduce call volume","Honor Retry-After headers on 403/429","Treat bare status-line errors as a signal the body was non-JSON (proxy/outage) and inspect headers"],"tags":["github","http","rate-limit","error-handling"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}