{"record":{"id":"d63dac324ddddd57","repo":"XTLS/Xray-core","slug":"unexpected-http-status-code","errorCode":null,"errorMessage":"unexpected HTTP status code: ","messagePattern":"unexpected HTTP status code: ","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"main/confloader/external/external.go","lineNumber":87,"sourceCode":"\t\t\tDialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\tvar d net.Dialer\n\t\t\t\treturn d.DialContext(ctx, \"unix\", dialAddr)\n\t\t\t},\n\t\t}\n\t}\n\n\tresp, err := client.Do(&http.Request{\n\t\tMethod: \"GET\",\n\t\tURL:    parsedTarget,\n\t\tClose:  true,\n\t})\n\tif err != nil {\n\t\treturn nil, errors.New(\"failed to dial to \", target).Base(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\treturn nil, errors.New(\"unexpected HTTP status code: \", resp.StatusCode)\n\t}\n\n\tcontent, err := buf.ReadAllToBytes(resp.Body)\n\tif err != nil {\n\t\treturn nil, errors.New(\"failed to read HTTP response\").Base(err)\n\t}\n\n\treturn content, nil\n}\n\n// isRemoteSource reports whether arg should be fetched via HTTP (regular\n// network or Unix socket) rather than read from the local filesystem.\n// Recognized forms:\n//\n//   - http(s)://...           regular HTTP(S)\n//   - @abstract[:/api]        abstract socket (Linux/Android)\n//   - /abs/path:/api          filesystem socket, explicit HTTP path\n//   - /abs/path               filesystem socket detected via os.ModeSocket","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/main/confloader/external/external.go#L69-L105","documentation":"The remote fetch in FetchHTTPContent succeeded at the transport level but the server answered with a status code other than 200. Xray requires exactly HTTP 200 for remote config content; any 3xx/4xx/5xx (redirects are not followed, since the request is hand-built) is rejected with this error. Unlike the other errors here it has no Base cause — the status code is embedded in the message.","triggerScenarios":"Fetching a config URL that returns 404 (wrong path), 401/403 (auth required), 500 (server error), or a 301/302 redirect (Xray does not follow redirects because it constructs http.Request directly with Close:true).","commonSituations":"Config URL path typo on the hosting side, a CDN or object store returning 403 without signed URLs, an API gateway demanding auth, or moving the config file without updating the -config argument.","solutions":["curl -i the exact URL to see the real status code and body","Fix the path to point at the actual config resource; use a direct 200-OK endpoint, not a redirect","If auth is required, put the config behind a plain static endpoint or fetch it yourself and pass the local file","For object storage/CDNs, disable redirect-style delivery or use the final direct URL"],"exampleFix":"# before\nxray run -config https://cdn.example.com/conf   # 301 -> unexpected HTTP status code: 301\n\n# after\nxray run -config https://cdn.example.com/conf/config.json","handlingStrategy":"validation","validationCode":"resp, err := http.Get(target) // pre-check\nif err == nil && resp.StatusCode == 200 { /* safe to pass to Xray */ }","typeGuard":null,"tryCatchPattern":"if err := load(); err != nil {\n    if strings.Contains(err.Error(), \"unexpected HTTP status code: 404\") { fixPathAndRetry() }\n}","preventionTips":["Use direct 200-OK static endpoints for remote config","Avoid redirect-based CDNs or signed-URL setups that 302/403","Monitor config endpoints with uptime checks"],"tags":["go","xray","http","status-code","config"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}