air-verse/air · error
proxy inject: failed to read body from http response: %w
Error message
proxy inject: failed to read body from http response: %w
What it means
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.
Source
Thrown at runner/proxy.go:137
decoded := false
switch detectContentEncoding(resp.Header) {
case encodingGzip:
gzipReader, err := gzip.NewReader(resp.Body)
if err != nil {
return "", false, fmt.Errorf("proxy inject: failed to init gzip reader: %w", err)
}
defer gzipReader.Close()
reader = gzipReader
decoded = true
case encodingBrotli:
reader = brotli.NewReader(resp.Body)
decoded = true
}
buf := new(bytes.Buffer)
if _, err := buf.ReadFrom(reader); err != nil {
return "", decoded, fmt.Errorf("proxy inject: failed to read body from http response: %w", err)
}
page := buf.String()
// 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.
body := strings.LastIndex(page, "</body>")
if body == -1 {
return page, decoded, nil
}
script := "<script>" + ProxyScript + "</script>"
return page[:body] + script + page[body:], decoded, nil
}
func (p *Proxy) proxyHandler(w http.ResponseWriter, r *http.Request) {
appURL := r.URL
appURL.Scheme = "http"
appURL.Host = fmt.Sprintf("localhost:%d", p.config.AppPort)
View on GitHub (pinned to 71ea1dee05)
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)
Example fix
// before: proxying to a remote, flaky target app_port = 3000 // remote host resets connections // after: proxy to a stable local dev server app_port = 8080 // local server that keeps connections open
Defensive patterns
Strategy: retry
Validate before calling
// check the app is up and stable before proxying
resp, err := http.Get("http://localhost:" + appPort + "/")
if err != nil {
return fmt.Errorf("app not reachable: %w", err)
}
io.Copy(io.Discard, resp.Body) // ensure body reads fully Try / catch
if err := startProxy(); err != nil && strings.Contains(err.Error(), "failed to read body") {
// app likely reset mid-response; wait and retry
time.Sleep(500 * time.Millisecond)
_ = startProxy()
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: The dev server crashes or closes connections mid-response while air is proxying; flaky upstream (hot-reload restarts); corrupt brotli-encoded responses.
Related errors
- proxy inject: failed to init gzip reader: %w
- proxy handler: bad form
- proxy handler: unable to create request
AI-assisted analysis of air-verse/air@71ea1dee05 (2026-08-31).
Data as JSON: /api/errors/3fcdcd35a983c9fa.
Report an issue: GitHub.