{"record":{"id":"3fcdcd35a983c9fa","repo":"air-verse/air","slug":"proxy-inject-failed-to-read-body-from-http-respon","errorCode":null,"errorMessage":"proxy inject: failed to read body from http response: %w","messagePattern":"proxy inject: failed to read body from http response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"runner/proxy.go","lineNumber":137,"sourceCode":"\tdecoded := false\n\n\tswitch detectContentEncoding(resp.Header) {\n\tcase encodingGzip:\n\t\tgzipReader, err := gzip.NewReader(resp.Body)\n\t\tif err != nil {\n\t\t\treturn \"\", false, fmt.Errorf(\"proxy inject: failed to init gzip reader: %w\", err)\n\t\t}\n\t\tdefer gzipReader.Close()\n\t\treader = gzipReader\n\t\tdecoded = true\n\tcase encodingBrotli:\n\t\treader = brotli.NewReader(resp.Body)\n\t\tdecoded = true\n\t}\n\n\tbuf := new(bytes.Buffer)\n\tif _, err := buf.ReadFrom(reader); err != nil {\n\t\treturn \"\", decoded, fmt.Errorf(\"proxy inject: failed to read body from http response: %w\", err)\n\t}\n\tpage := buf.String()\n\n\t// the script will be injected before the end of the body tag. In case the tag is missing, the injection will be skipped with no error.\n\tbody := strings.LastIndex(page, \"</body>\")\n\tif body == -1 {\n\t\treturn page, decoded, nil\n\t}\n\n\tscript := \"<script>\" + ProxyScript + \"</script>\"\n\treturn page[:body] + script + page[body:], decoded, nil\n}\n\nfunc (p *Proxy) proxyHandler(w http.ResponseWriter, r *http.Request) {\n\tappURL := r.URL\n\tappURL.Scheme = \"http\"\n\tappURL.Host = fmt.Sprintf(\"localhost:%d\", p.config.AppPort)\n","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/air-verse/air/blob/71ea1dee05248122ed22b5870c7fb20a90d80ddd/runner/proxy.go#L119-L155","documentation":"After optionally decompressing, injectLiveReload reads the entire response body into a buffer so the live-reload script can be injected before </body>. Any I/O failure reading the body is wrapped in this error.","triggerScenarios":"injectLiveReload (called from proxyHandler) gets an I/O error while buf.ReadFrom reads the upstream body — connection reset by the app mid-response, aborted chunked encoding, or a failing brotli reader on a corrupt stream.","commonSituations":"The dev server crashes or closes connections mid-response while air is proxying; flaky upstream (hot-reload restarts); corrupt brotli-encoded responses.","solutions":["Check the wrapped %w error for the underlying I/O cause and whether the upstream app crashed mid-response","Restart/rebuild the dev application so it serves complete responses","Disable upstream compression (brotli/gzip) if brotli streams are the trigger","Check network paths between air's proxy and the app (localhost should be reliable; remote targets less so)"],"exampleFix":"// before: proxying to a remote, flaky target\napp_port = 3000 // remote host resets connections\n// after: proxy to a stable local dev server\napp_port = 8080 // local server that keeps connections open","handlingStrategy":"retry","validationCode":"// check the app is up and stable before proxying\nresp, err := http.Get(\"http://localhost:\" + appPort + \"/\")\nif err != nil {\n\treturn fmt.Errorf(\"app not reachable: %w\", err)\n}\nio.Copy(io.Discard, resp.Body) // ensure body reads fully","typeGuard":null,"tryCatchPattern":"if err := startProxy(); err != nil && strings.Contains(err.Error(), \"failed to read body\") {\n\t// app likely reset mid-response; wait and retry\n\ttime.Sleep(500 * time.Millisecond)\n\t_ = startProxy()\n}","preventionTips":["Ensure the dev server finishes responses before restarting","Avoid flaky remote targets behind air's proxy; use localhost","Disable upstream brotli/gzip in development","Watch app logs for crashes coinciding with proxy errors"],"tags":["proxy","http","network"],"backgroundTag":"upstream-read-failure","analyzedSha":"71ea1dee05248122ed22b5870c7fb20a90d80ddd","analyzedAt":"2026-08-31T20:27:54.676Z","schemaVersion":2},"datasetVersion":"2026-09-01T03:17:15.561Z"}