{"record":{"id":"8f73465709570e64","repo":"golang/go","slug":"bufio-reader-returned-negative-count-from-read","errorCode":null,"errorMessage":"bufio: reader returned negative count from Read","messagePattern":"bufio: reader returned negative count from Read","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/bufio/bufio.go","lineNumber":96,"sourceCode":"\tif b == r {\n\t\treturn\n\t}\n\tif b.buf == nil {\n\t\tb.buf = make([]byte, defaultBufSize)\n\t}\n\tb.reset(b.buf, r)\n}\n\nfunc (b *Reader) reset(buf []byte, r io.Reader) {\n\t*b = Reader{\n\t\tbuf:          buf,\n\t\trd:           r,\n\t\tlastByte:     -1,\n\t\tlastRuneSize: -1,\n\t}\n}\n\nvar errNegativeRead = errors.New(\"bufio: reader returned negative count from Read\")\n\n// fill reads a new chunk into the buffer.\nfunc (b *Reader) fill() {\n\t// Slide existing data to beginning.\n\tif b.r > 0 {\n\t\tcopy(b.buf, b.buf[b.r:b.w])\n\t\tb.w -= b.r\n\t\tb.r = 0\n\t}\n\n\tif b.w >= len(b.buf) {\n\t\tpanic(\"bufio: tried to fill full buffer\")\n\t}\n\n\t// Read new data: try a limited number of times.\n\tfor i := maxConsecutiveEmptyReads; i > 0; i-- {\n\t\tn, err := b.rd.Read(b.buf[b.w:])\n\t\tif n < 0 {","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/bufio/bufio.go#L78-L114","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — buggy reader returns negative n\nfunc (b *BadReader) Read(p []byte) (int, error) {\n    if b.err != nil {\n        return -1, b.err // WRONG: violates io.Reader contract\n    }\n    return b.src.Read(p)\n}\n\n// after — return 0 with the error\nfunc (b *BadReader) Read(p []byte) (int, error) {\n    if b.err != nil {\n        return 0, b.err\n    }\n    return b.src.Read(p)\n}","handlingStrategy":"validation","validationCode":"// Wrap any custom io.Reader before passing to bufio to guarantee the Read contract.\ntype safeReader struct{ io.Reader }\nfunc (s safeReader) Read(p []byte) (int, error) {\n    n, err := s.Reader.Read(p)\n    if n < 0 {\n        return 0, fmt.Errorf(\"underlying reader returned negative count %d: %w\", n, err)\n    }\n    return n, err\n}\n// Usage:\n// r := bufio.NewReader(safeReader{myCustomReader})","typeGuard":"// Contract checker for io.Reader implementations (use in tests).\nfunc assertReaderContract(r io.Reader) error {\n    probe := make([]byte, 8)\n    n, err := r.Read(probe)\n    if n < 0 || n > len(probe) {\n        return fmt.Errorf(\"reader violates contract: n=%d\", n)\n    }\n    return err\n}","tryCatchPattern":"// Go has no try/catch; use defer/recover only as a last-resort guard around bufio use\ndefer func() {\n    if r := recover(); r != nil {\n        if msg, ok := r.(string); ok && strings.Contains(msg, \"negative count from Read\") {\n            // log and exit gracefully; the underlying reader is buggy\n        }\n        panic(r) // re-panic unknown\n    }\n}()\nn, err := bufioReader.Read(buf)","preventionTips":["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."],"tags":["go","bufio","io","panic","reader"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}