gofiber/fiber · error
failed to write response body to writer: %w
Error message
failed to write response body to writer: %w
What it means
Returned by Response.Save (client/response.go:178) when io.Copy fails while streaming the response body into a caller-supplied io.Writer. Save accepts an io.Writer (e.g. *os.File, *bytes.Buffer, a network connection) and copies BodyStream() into it; any write error on the destination, or read error on the stream, is wrapped here.
Source
Thrown at client/response.go:178
// Create and write to file
outFile, err := os.Create(file)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer func() { _ = outFile.Close() }() //nolint:errcheck // not needed
// Use BodyStream() which handles both streaming and non-streaming cases
if _, err = io.Copy(outFile, r.BodyStream()); err != nil {
return fmt.Errorf("failed to write response body to file: %w", err)
}
return nil
case io.Writer:
// Use BodyStream() which handles both streaming and non-streaming cases
if _, err := io.Copy(p, r.BodyStream()); err != nil {
return fmt.Errorf("failed to write response body to writer: %w", err)
}
// Close the writer if it implements io.WriteCloser
if pc, ok := p.(io.WriteCloser); ok {
_ = pc.Close() //nolint:errcheck // not needed
}
return nil
default:
return ErrNotSupportSaveMethod
}
}
// Reset clears the Response object, making it ready for reuse.
func (r *Response) Reset() {
r.client = nil
r.request = nil
View on GitHub (pinned to 9a4c7e57fe)
Solutions
- Ensure the destination Writer's downstream stays alive for the full copy (keep the connection/reader open).
- Use an unbounded or sufficiently large buffer if the destination can stall.
- Inspect the wrapped error to distinguish a destination-write error from a source-stream error.
- Retry the upstream request if the source stream broke.
Example fix
// before — piping to a connection that may close mid-stream
if err := resp.Save(downstreamConn); err != nil {
log.Print(err) // half-written downstream response
}
// after — buffer then forward, or handle the broken-pipe error explicitly
var buf bytes.Buffer
if err := resp.Save(&buf); err != nil {
return fmt.Errorf("read response body: %w", err)
}
if _, err := downstreamConn.Write(buf.Bytes()); err != nil {
return fmt.Errorf("forward body: %w", err)
} Defensive patterns
Strategy: try-catch
Type guard
// Ensure the destination implements io.Writer and is not prematurely closed.
func liveWriter(w any) bool {
_, ok := w.(io.Writer)
return ok
} Try / catch
if err := resp.Save(w); err != nil {
if strings.Contains(err.Error(), "write response body to writer") {
// downstream writer failed — buffer and handle separately
}
return err
} Prevention
- Keep the destination Writer's downstream alive for the full copy.
- Use a sufficiently large buffer if the destination can stall.
- Distinguish source-stream errors from destination-write errors via the wrapped error.
When it happens
Trigger: Calling resp.Save(writer) where writer is a network connection that closed, a pipe whose reader exited, a buffer that hit a hard cap, or any io.Writer whose Write returns an error. Also triggered when the body stream errors during the copy (server cut the connection, corrupt gzip).
Common situations: Streaming a download straight to an HTTP client whose connection dropped; piping through a writer whose downstream consumer died; a bounded buffer overflowing; proxying a large response that the next hop rejects mid-stream.
Related errors
- failed to write response body to file: %w
- failed to check directory: %w
- failed to create directory: %w
- failed to create file: %w
- format: at least one handler is required, but none were set
AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04).
Data as JSON: /data/errors/ce61eb421f2c2b6c.json.
Report an issue: GitHub.