{"record":{"id":"5497f2281cc76f69","repo":"XTLS/Xray-core","slug":"unexpected-status-code","errorCode":null,"errorMessage":"unexpected status code: ","messagePattern":"unexpected status code: ","errorType":"console","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"app/geodata/download.go","lineNumber":213,"sourceCode":"\t\treturn err\n\t}\n\tutils.TryDefaultHeadersWith(req.Header, \"nav\")\n\n\tvar client *http.Client\n\tif req.URL.Scheme == \"https\" {\n\t\tclient = d.httpsClient\n\t} else {\n\t\tclient = d.httpClient\n\t}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {\n\t\tio.Copy(io.Discard, resp.Body)\n\t\treturn errors.New(\"unexpected status code: \", resp.StatusCode)\n\t}\n\n\tn, err := io.Copy(writer, resp.Body)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif n == 0 {\n\t\treturn errors.New(\"empty response body\")\n\t}\n\treturn nil\n}\n\nfunc clean(assets []stage) {\n\tfor _, asset := range assets {\n\t\tif asset.temp != \"\" {\n\t\t\tos.Remove(asset.temp)\n\t\t}\n\t}","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/geodata/download.go#L195-L231","documentation":"Returned by downloadOne after a geodata HTTP response arrives with a status code outside 200-299. The body is drained/discarded and the numeric status is embedded in the message.","triggerScenarios":"client.Do for a geodata asset returns e.g. 404 (file removed / wrong path), 403 (rate limited or blocked CDN), 5xx (mirror outage), or 301 leaking through when redirects were stopped.","commonSituations":"Wrong asset URL path after a repo reorganization; GitHub raw rate limiting; mirror temporarily down; asset name typo like 'geosites.dat' instead of 'geosite.dat'.","solutions":["Open the exact asset URL in a browser/curl to confirm it returns 200","For 403/429 rate limiting, switch mirrors or add delay between updates (widen the geodata cron)","For 404, correct the asset path or use the default asset URLs","Retry later for 5xx outages"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Health-check the asset URL before the cron fires:\nresp, err := http.Head(assetURL)\nif err != nil || resp.StatusCode < 200 || resp.StatusCode >= 300 {\n    // mark mirror unhealthy, use fallback URL\n}","typeGuard":null,"tryCatchPattern":"resp, err := client.Do(req)\nif err == nil && (resp.StatusCode < 200 || resp.StatusCode >= 300) {\n    switch resp.StatusCode {\n    case 403, 429:\n        time.Sleep(time.Minute) // rate limited: back off before retry\n    case 404:\n        return fmt.Errorf(\"asset missing at %s; fix URL\", req.URL) // permanent\n    default:\n        // transient 5xx: retry later\n    }\n}","preventionTips":["Use official, stable mirrors for geodata assets","Space geodata updates via cron to avoid rate limits","Verify asset URLs return 200 during config review"],"tags":["network","geodata","http-status","download"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}