valyala/fasthttp · error
bufio read returned (%d, nil)
Error message
bufio read returned (%d, nil)
What it means
This error is returned by readBodyIdentity in fasthttp when the underlying bufio.Reader.Read call violates the io.Reader contract by returning 0 bytes read with a nil error. The io.Reader contract requires that a Read returning n <= 0 must also return a non-nil error, so this guards against a misbehaving underlying reader (custom conns, wrapped transports). It is a defensive invariant check, not a protocol error.
Source
Thrown at http.go:2879
return b, nil
}
func readBodyIdentity(r *bufio.Reader, maxBodySize int, dst []byte) ([]byte, error) {
dst = dst[:cap(dst)]
if len(dst) == 0 {
dst = make([]byte, 1024)
}
offset := 0
for {
nn, err := r.Read(dst[offset:])
if nn <= 0 {
switch {
case errors.Is(err, io.EOF):
return dst[:offset], nil
case err != nil:
return dst[:offset], err
default:
return dst[:offset], fmt.Errorf("bufio read returned (%d, nil)", nn)
}
}
offset += nn
if maxBodySize > 0 && offset > maxBodySize {
return dst[:offset], ErrBodyTooLarge
}
if len(dst) == offset {
n := roundUpForSliceCap(2 * offset)
if maxBodySize > 0 && n > maxBodySize {
n = maxBodySize + 1
}
b := make([]byte, n)
copy(b, dst)
dst = b
}
}
}
View on GitHub (pinned to c96f600972)
Solutions
- Fix the custom io.Reader/net.Conn wrapper so it never returns n==0 with err==nil; return io.EOF at end of stream instead.
- Check recently added middleware or conn wrappers and replace them with a contract-conforming implementation.
- If it reproduces with stock fasthttp and no wrappers, report it to valyala/fasthttp with a minimal repro.
- As a workaround, use a plain connection without the custom wrapper to confirm it is the source.
Example fix
// before (bad custom reader)
func (r *myConn) Read(p []byte) (int, error) {
if r.pending == 0 { return 0, nil } // violates io.Reader contract
...
}
// after
func (r *myConn) Read(p []byte) (int, error) {
if r.pending == 0 { return 0, io.EOF }
...
} Defensive patterns
Strategy: try-catch
Validate before calling
// Audit any custom io.Reader/net.Conn wrappers before enabling:
// they must never return (0, nil).
func checkReader(r io.Reader) error {
buf := make([]byte, 1)
n, err := r.Read(buf)
if n == 0 && err == nil {
return errors.New("reader violates io.Reader contract: (0, nil)")
}
return nil
} Type guard
if err != nil && strings.Contains(err.Error(), "bufio read returned") {
// invariant violation: reader returned (0, nil); not an ErrBrokenChunk
} Try / catch
b, err := ... // body read
if err != nil {
if strings.Contains(err.Error(), "bufio read returned") {
log.Printf("underlying reader violates io.Reader contract: %v", err)
// replace/inspect custom conn wrapper
}
return err
} Prevention
- Never implement io.Reader wrappers that return (0, nil); always return io.EOF or a real error with zero bytes.
- Fuzz-test custom connection wrappers against the io.Reader contract before deploying.
- Prefer wrapping net.Conn at the transport level with well-tested libraries.
- Keep fasthttp updated; watch changelogs for body-reading fixes.
When it happens
Trigger: Reading an identity-encoded (non-chunked) request/response body via Server/Client when the conn's bufio.Reader.Read returns (0, nil) — typically caused by a custom net.Conn or a wrapper implementing io.Reader incorrectly.
Common situations: Custom transport/wrapper code (compression, logging, TLS-shim conns) around the connection returning zero bytes without an error; testing with mocked conns that don't follow the io.Reader contract.
Related errors
- cannot unread '\r' char at the end of chunk size: %w
- fasthttp: no args value for the given key
- fasthttp: pipelined requests' queue has been overflowed. inc
- pipeline connection has been stopped
- fasthttp: no cookies found
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/2d485a274710fc90.
Report an issue: GitHub.