golang/go · critical
bufio: reader returned negative count from Read
Error message
bufio: reader returned negative count from Read
What it means
Go's bufio package panics with this message inside Reader.fill() when the underlying io.Reader's Read method returns a negative byte count n. The io.Reader contract (documented in package io) mandates 0 <= n <= len(p); a negative value violates the contract so severely that bufio treats it as a programming bug in the reader implementation and aborts rather than continuing with corrupted buffer indices. This is an unrecoverable panic, not a returned error.
Source
Thrown at src/bufio/bufio.go:96
if b == r {
return
}
if b.buf == nil {
b.buf = make([]byte, defaultBufSize)
}
b.reset(b.buf, r)
}
func (b *Reader) reset(buf []byte, r io.Reader) {
*b = Reader{
buf: buf,
rd: r,
lastByte: -1,
lastRuneSize: -1,
}
}
var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
// fill reads a new chunk into the buffer.
func (b *Reader) fill() {
// Slide existing data to beginning.
if b.r > 0 {
copy(b.buf, b.buf[b.r:b.w])
b.w -= b.r
b.r = 0
}
if b.w >= len(b.buf) {
panic("bufio: tried to fill full buffer")
}
// Read new data: try a limited number of times.
for i := maxConsecutiveEmptyReads; i > 0; i-- {
n, err := b.rd.Read(b.buf[b.w:])
if n < 0 {View on GitHub (pinned to b6b368adc5)
Solutions
- Inspect the concrete io.Reader passed to bufio.NewReader — find its Read method and fix the negative return; Read must return n >= 0 and a non-nil error to signal trouble.
- If you cannot fix the upstream reader, wrap it in an adapter that clamps n to 0 and synthesizes an error when the inner Read returns negative.
- Add a unit test that feeds the suspect reader into a bufio.Reader and asserts no panic under error paths.
- Run `go vet` and review the io.Reader implementation against the contract in `io` package docs.
Example fix
// before — buggy reader returns negative n
func (b *BadReader) Read(p []byte) (int, error) {
if b.err != nil {
return -1, b.err // WRONG: violates io.Reader contract
}
return b.src.Read(p)
}
// after — return 0 with the error
func (b *BadReader) Read(p []byte) (int, error) {
if b.err != nil {
return 0, b.err
}
return b.src.Read(p)
} Defensive patterns
Strategy: validation
Validate before calling
// Wrap any custom io.Reader before passing to bufio to guarantee the Read contract.
type safeReader struct{ io.Reader }
func (s safeReader) Read(p []byte) (int, error) {
n, err := s.Reader.Read(p)
if n < 0 {
return 0, fmt.Errorf("underlying reader returned negative count %d: %w", n, err)
}
return n, err
}
// Usage:
// r := bufio.NewReader(safeReader{myCustomReader}) Type guard
// Contract checker for io.Reader implementations (use in tests).
func assertReaderContract(r io.Reader) error {
probe := make([]byte, 8)
n, err := r.Read(probe)
if n < 0 || n > len(probe) {
return fmt.Errorf("reader violates contract: n=%d", n)
}
return err
} Try / catch
// Go has no try/catch; use defer/recover only as a last-resort guard around bufio use
defer func() {
if r := recover(); r != nil {
if msg, ok := r.(string); ok && strings.Contains(msg, "negative count from Read") {
// log and exit gracefully; the underlying reader is buggy
}
panic(r) // re-panic unknown
}
}()
n, err := bufioReader.Read(buf) Prevention
- Never return a negative n from a custom io.Reader.Read — return 0 and a non-nil error instead.
- Unit-test custom readers against bufio.NewReader to confirm no panic on error paths.
- Review every io.Reader implementation against the contract in package io docs.
When it happens
Trigger: Triggered when bufio.Reader.fill() calls b.rd.Read(buf) and the returned n is < 0 (bufio.go:114-115). Any call that forces a buffer refill — Read, ReadByte, ReadRune, ReadString, Peek, Discard, WriteTo — can reach fill() and trip the panic. The immediate cause is always the wrapped io.Reader returning a negative count.
Common situations: A custom io.Reader implementation has a bug computing the return count (e.g., returns -1 to signal an internal error instead of returning 0 with a proper error). A wrapped reader that subtracts offsets incorrectly, or a middleware reader (compressed/encrypted stream) whose internal accounting overflows. Rarely seen with stdlib readers; almost always a hand-rolled Reader.
Related errors
- bufio: writer returned negative count from Write
- bufio.Scanner: token too long
- bufio.Scanner: SplitFunc returns negative advance count
- bufio.Scanner: SplitFunc returns advance count beyond input
- final token
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/8f73465709570e64.
Report an issue: GitHub.