golang/go · critical

bufio: writer returned negative count from Write

Error message

bufio: writer returned negative count from Write

What it means

Go's bufio package panics with this message inside Reader.writeBuf() when an io.Writer's Write method returns a negative byte count n while draining the Reader's buffer. The io.Writer contract requires 0 <= n <= len(p); a negative value corrupts the read pointer arithmetic, so bufio aborts via panic. This is unrecoverable — it indicates a bug in the writer implementation, not a normal I/O failure.

Source

Thrown at src/bufio/bufio.go:559

	for b.r < b.w {
		// b.r < b.w => buffer is not empty
		m, err := b.writeBuf(w)
		n += m
		if err != nil {
			return n, err
		}
		b.fill() // buffer is empty
	}

	if b.err == io.EOF {
		b.err = nil
	}

	return n, b.readErr()
}

var errNegativeWrite = errors.New("bufio: writer returned negative count from Write")

// writeBuf writes the [Reader]'s buffer to the writer.
func (b *Reader) writeBuf(w io.Writer) (int64, error) {
	n, err := w.Write(b.buf[b.r:b.w])
	if n < 0 {
		panic(errNegativeWrite)
	}
	b.r += n
	return int64(n), err
}

// buffered output

// Writer implements buffering for an [io.Writer] object.
// If an error occurs writing to a [Writer], no more data will be
// accepted and all subsequent writes, and [Writer.Flush], will return the error.
// After all data has been written, the client should call the
// [Writer.Flush] method to guarantee all data has been forwarded to

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Find the concrete io.Writer being written to (the argument to WriteTo / io.Copy target) and fix its Write method to return n >= 0.
  2. If fixing upstream is not possible, wrap the writer so a negative n is clamped to 0 and a synthetic error is returned.
  3. Add a regression test that writes through the suspect writer and verifies n stays non-negative across error paths.
  4. Re-read the io.Writer contract in package io docs: 'Write must return a non-negative number of bytes'.

Example fix

// before — buggy writer returns negative n
func (w *BadWriter) Write(p []byte) (int, error) {
    if w.closed {
        return -1, ErrClosed // WRONG
    }
    return w.dest.Write(p)
}

// after — return 0 with the error
func (w *BadWriter) Write(p []byte) (int, error) {
    if w.closed {
        return 0, ErrClosed
    }
    return w.dest.Write(p)
}
Defensive patterns

Strategy: validation

Validate before calling

// Wrap any custom io.Writer before it receives data from a bufio.Reader.
type safeWriter struct{ io.Writer }
func (s safeWriter) Write(p []byte) (int, error) {
    n, err := s.Writer.Write(p)
    if n < 0 {
        return 0, fmt.Errorf("underlying writer returned negative count %d: %w", n, err)
    }
    return n, err
}
// Usage:
// _, err := bufioReader.WriteTo(safeWriter{myCustomWriter})

Type guard

// Contract checker for io.Writer implementations.
func assertWriterContract(w io.Writer) error {
    n, err := w.Write([]byte("x"))
    if n < 0 || n > 1 {
        return fmt.Errorf("writer violates contract: n=%d", n)
    }
    return err
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        if msg, ok := r.(string); ok && strings.Contains(msg, "negative count from Write") {
            // handle: the target writer is buggy
        }
        panic(r)
    }
}()
_, err := bufioReader.WriteTo(w)

Prevention

When it happens

Trigger: Triggered when Reader.WriteTo (bufio.go:562-566) calls w.Write(b.buf[b.r:b.w]) and the returned n is < 0. Reachable through Reader.WriteTo, and indirectly via io.Copy when the source is a bufio.Reader. The culprit is always the concrete io.Writer returning a negative count.

Common situations: A custom io.Writer returns -1 or another negative number to flag an internal error instead of returning 0 bytes with a non-nil error. A writer wrapper (e.g., a counting/tee writer) that computes n with an underflowing subtraction. Stdlib writers do not produce this; it is characteristic of hand-rolled Writer implementations.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/24242c5b069679c8. Report an issue: GitHub.