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

  1. Make the declared size exactly match the bytes the stream will produce (or pass -1 to use chunked transfer).
  2. Compute sizes on already-final data (e.g. len(compressedBytes)) not estimates.
  3. Fix the reader to return io.EOF only after emitting all promised bytes.
  4. 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

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


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/a153eabfb1e0b38e. Report an issue: GitHub.