{"record":{"id":"b2b9ed2565c42280","repo":"XTLS/Xray-core","slug":"failed-to-read-http-response-b2b9ed","errorCode":null,"errorMessage":"failed to read HTTP response","messagePattern":"failed to read HTTP response","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"main/confloader/external/external.go","lineNumber":92,"sourceCode":"\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\nfunc isRemoteSource(arg string) bool {\n\tif arg == \"\" {\n\t\treturn false\n\t}\n\tif strings.HasPrefix(arg, \"http://\") || strings.HasPrefix(arg, \"https://\") {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/main/confloader/external/external.go#L74-L110","documentation":"Raised after a successful 200 response when buf.ReadAllToBytes fails while draining resp.Body. This means the connection broke mid-transfer: the server (or an intermediary) closed the stream early, the read exceeded the 30-second client timeout, or the body was truncated. The underlying error is chained with Base(err).","triggerScenarios":"Server closes the connection partway through sending the config; network drops the TCP connection; a slow config endpoint pushes a large body past the fixed 30s http.Client timeout; socket-side server crashes mid-write.","commonSituations":"Large config files served by slow dynamic endpoints (API generating config on the fly), flaky wireless links, proxies/LBs with aggressive idle timeouts, or a Unix-socket API that dies during serialization.","solutions":["Check the chained Base error: 'unexpected EOF' means truncated body, 'context deadline exceeded' means the 30s timeout hit","Retry the fetch — transient truncation often resolves itself","Reduce response size or speed up the server so the full body streams well under 30 seconds","If the timeout is the persistent problem, fetch the content externally and pass a local file to Xray"],"exampleFix":"// before: remote fetch raced a 30s timeout\nerr := confloader.ExtLoadConfig(\"http://slow-host/big-config.json\")\n\n// after: pre-fetch with a longer timeout, then load locally\nresp, err := (&http.Client{Timeout: 5 * time.Minute}).Get(url) // save to file...\nerr = confloader.ExtLoadConfig(\"/tmp/big-config.json\")","handlingStrategy":"retry","validationCode":"// fetch with your own client and a generous timeout, verify length, then pass local file\nc := &http.Client{Timeout: 5 * time.Minute}\nif b, err := c.Get(url); err == nil && b.ContentLength > 0 { /* cache to disk, load from disk */ }","typeGuard":null,"tryCatchPattern":"if errors.Is(err, io.ErrUnexpectedEOF) || strings.Contains(err.Error(), \"unexpected EOF\") { retryWithBackoff() }","preventionTips":["Serve configs from fast static endpoints","Keep config bodies small (split large assets out of config)","Fall back to the last known-good cached config file on fetch failure"],"tags":["go","xray","http","timeout","io","config"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}