valyala/fasthttp · error
copied %d bytes from body stream instead of %d bytes
Error message
copied %d bytes from body stream instead of %d bytes
What it means
After copying a response body stream to the wire, fasthttp compares the number of bytes copied with the declared Content-Length/size; if they differ without another error, it reports this mismatch. The transfer was malformed — the body did not contain the promised number of bytes — so the response/request is invalid (e.g. chunked framing or connection will be closed).
Source
Thrown at http.go:2655
switch r := r.(type) {
case *os.File:
earlyFlush = true
case *io.LimitedReader:
_, earlyFlush = r.R.(*os.File)
}
if earlyFlush {
// w buffer must be empty for triggering
// sendfile path in bufio.Writer.ReadFrom.
if err := w.Flush(); err != nil {
return err
}
}
}
n, err := copyBodyStream(w, r)
if n != size && err == nil {
err = fmt.Errorf("copied %d bytes from body stream instead of %d bytes", n, size)
}
return err
}
func copyBodyStream(w io.Writer, r io.Reader) (int64, error) {
if bwt, ok := r.(BodyWriterTo); ok {
if bwt.SupportsBodyWriteTo() {
return bwt.WriteTo(w)
}
vbuf := copyBufPool.Get()
buf := vbuf.([]byte) //nolint:forcetypeassert
n, err := copyBuffer(w, r, buf)
copyBufPool.Put(vbuf)
return n, err
}
return copyZeroAlloc(w, r)View on GitHub (pinned to c96f600972)
Solutions
- Make the declared size exactly match the bytes the stream will produce (or pass -1 to use chunked transfer).
- Compute sizes on already-final data (e.g. len(compressedBytes)) not estimates.
- Fix the reader to return io.EOF only after emitting all promised bytes.
- Inspect the wrapped copy error first — often a real read error accompanies the mismatch.
Example fix
// before resp.SetBodyStream(gzippedReader, len(rawData)) // wrong size // after var buf bytes.Buffer zw := gzip.NewWriter(&buf) zw.Write(rawData); zw.Close() resp.SetBodyStream(bytes.NewReader(buf.Bytes()), buf.Len())
Defensive patterns
Strategy: validation
Validate before calling
var buf bytes.Buffer
n, err := io.Copy(&buf, stream)
if err != nil { return err }
if int64(n) != declaredSize { return fmt.Errorf("stream yields %d bytes, declared %d", n, declaredSize) } Type guard
func exactReader(r io.Reader, size int64) io.Reader {
return io.LimitReader(r, size) // pair with verifying total bytes produced
} Try / catch
if err := client.Do(req, resp); err != nil {
if strings.Contains(err.Error(), "instead of") &&
strings.Contains(err.Error(), "body stream") {
return fmt.Errorf("declared size mismatch; use -1 for chunked: %w", err)
}
return err
} Prevention
- Pass -1 to SetBodyStream to use chunked encoding when size is unknown
- Buffer and measure generated bodies before declaring a size
- Never use uncompressed size as the hint for compressed streams
- Fix readers that short-read and return io.EOF early
When it happens
Trigger: resp.SetBodyStream(r, N) where r yields fewer (or more) than N bytes; writeBodyStream's copyBodyStream returns n != size with err == nil (stream hit EOF early or produced extra data).
Common situations: Computing the byte count of a generated body incorrectly (uncompressed size vs gzip size); reader short-reads returning io.EOF before N bytes; using a compressed stream length as the size hint for uncompressed data.
Related errors
- panic while writing body stream: %+v
- fasthttp: cannot follow a body-preserving redirect for a req
- fasthttp: need more data: cannot find trailing lf
- cannot parse content-length: %w
- form size must be greater than 0: given %d
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/a153eabfb1e0b38e.
Report an issue: GitHub.