OpenNHP/opennhp · error
could not read response body
Error message
could not read response body: %v
What it means
After a 200 response, the function reads the full response body with io.ReadAll(resp.Body). If the read fails mid-stream — connection dropped or reset before the body finished arriving, chunked-encoding error, or an idle/keep-alive read timeout — it wraps the error as "could not read response body: %v". The upload itself likely succeeded server-side but the client cannot confirm it.
Solutions
- Retry the whole upload when the error is io.ErrUnexpectedEOF or a connection-reset, since the server state is uncertain (idempotency permitting)
- Check network path stability (VPN/proxy idle timeouts) and raise proxy keep-alive/idle timeouts above the upload duration
- Check server logs to see whether the upload was persisted despite the truncated response
- Verify the server writes and flushes its response promptly after processing instead of holding the connection
- If using a custom Transport, ensure ResponseHeaderTimeout/IdleConnTimeout are generous for long uploads
Example fix
// before
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("could not read response body: %v", err)
}
// after
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) {
return "", fmt.Errorf("response truncated, upload result unknown, retry: %v", err)
}
return "", fmt.Errorf("could not read response body: %v", err)
} Defensive patterns
Strategy: retry
Validate before calling
client := &http.Client{
Timeout: 120 * time.Minute,
Transport: &http.Transport{
IdleConnTimeout: 300 * time.Second,
ResponseHeaderTimeout: 120 * time.Second,
},
}
_ = client // use generous timeouts so keep-alive reads are not cut short Try / catch
result, err := device.UploadFileToNHPServer(filePath)
if err != nil && strings.Contains(err.Error(), "could not read response body") {
if strings.Contains(err.Error(), "unexpected EOF") || strings.Contains(err.Error(), "connection reset") {
// server may have persisted the file; verify existence before blind re-upload
return retryUploadWithBackoff(filePath)
}
return fmt.Errorf("failed reading upload confirmation: %w", err)
} Prevention
- Raise proxy/load-balancer idle timeouts above the longest upload duration
- Retry idempotently: verify whether the file landed on the server before re-uploading
- Avoid proxies that buffer/rewrite long-running uploads when possible
- Monitor server logs for crashes or truncation right after upload completion
- Use a transport with generous IdleConnTimeout for large transfers
When it happens
Trigger: io.ReadAll(resp.Body) returns a non-EOF error in UploadFileToNHPServer (endpoints/db/utils.go:271-274) on a 200 response: unexpected EOF from a connection reset after headers, server closing the connection while streaming the response, intermediate proxy truncating the response, or read timeout on a stalled keep-alive connection.
Common situations: Flaky network or NAT dropping the connection right after the upload completes; proxy/load balancer with a short idle timeout cutting the response; server crashing mid-response; very large 200 responses interrupted on slow links.
Related errors
- could not send https request
- failed to read HRK data
- failed to read response body
- failed to download ztdo
- could not copy file to server
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/661a320bf4ccd9fa.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/db/utils.go:273
Timeout: 120 * time.Minute,
}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("could not send https request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("unexpected status code: %d, content: %s", resp.StatusCode, string(bodyBytes))
}
// read response body
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("could not read response body: %v", err)
}
// parse response body
var respBody ServerResponse
err = json.Unmarshal(bodyBytes, &respBody)
if err != nil {
return "", fmt.Errorf("could not parse response body: %v", err)
}
duration := time.Since(startTime)
speed := float64(progress.TotalSize) / duration.Seconds() / (1024 * 1024)
// change the chinese to english
fmt.Printf("\nUpload %s to %s success! (time: %.2fs, speed: %.2fMB/s)\n",
filePath, httpHost+respBody.FileURI, duration.Seconds(), speed)
return httpHost + respBody.FileURI, nilView on GitHub (pinned to 6e04ca5ff0)