golang/go · error
bytes.Buffer: UnreadByte: previous operation was not a…
Error message
bytes.Buffer: UnreadByte: previous operation was not a successful read
What it means
bytes.Buffer.UnreadByte returns errUnreadByte when the most recent operation was not a successful read (buffer.go:425-428, guarded by b.lastRead == opInvalid). lastRead is opInvalid after writes, after Reset, before any read, or after an unread has already been consumed. Unlike UnreadRune, UnreadByte accepts any successful read op (opRead or opReadRuneX).
Solutions
- Ensure the immediately preceding successful Buffer operation was a read before calling UnreadByte.
- Avoid double-unread: only one pushback is valid; track it in parser state.
- If you must unread after a write, restructure so the write happens after the unread, or re-read from a known offset.
- Pair each UnreadByte call with a specific preceding read in the code so the invariant is locally obvious.
Example fix
// before — write between read and UnreadByte clears lastRead
b, _ := buf.ReadByte()
buf.WriteByte('x')
buf.UnreadByte() // error: previous op was not a successful read
// after — unread before the write, or re-read
b, _ := buf.ReadByte()
if pushBack { buf.UnreadByte() }
buf.WriteByte('x') Defensive patterns
Strategy: validation
Validate before calling
// Pair UnreadByte with a known preceding read.
if _, err := buf.ReadByte(); err != nil { return err }
// ... decision ...
if shouldPushBack {
if err := buf.UnreadByte(); err != nil {
// unexpected: lastRead should be opRead here
return err
}
} Try / catch
if err := buf.UnreadByte(); err != nil {
if err.Error() == "bytes.Buffer: UnreadByte: previous operation was not a successful read" {
// re-read from a known offset instead of unread
} else {
return err
}
} Prevention
- Ensure the immediately preceding Buffer op was a successful read before UnreadByte.
- Never call UnreadByte twice without an intervening read.
- Avoid interleaving writes between the read and the unread.
When it happens
Trigger: Triggered by calling buffer.UnreadByte() when lastRead == opInvalid: after a Write/WriteString/WriteByte, after Reset, before the first read, or after a prior UnreadByte/UnreadRune already reset lastRead to opInvalid.
Common situations: Interleaving a write between a read and UnreadByte. Calling UnreadByte twice in a row (the second call sees opInvalid). Speculative unread in a tokenizer without verifying the prior op was a read.
Related errors
- bytes.Buffer: UnreadRune: previous operation was not a…
- bytes.Buffer.Grow: negative count
- bytes.Buffer: truncation out of range
- bytes.Reader.UnreadByte: at beginning of slice
- bytes.Reader.UnreadRune: at beginning of slice
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/00fa1888ae5f58bd.
Report an issue: GitHub.
Appendix: source
Thrown at src/bytes/buffer.go:425
}
// UnreadRune unreads the last rune returned by [Buffer.ReadRune].
// If the most recent read or write operation on the buffer was
// not a successful [Buffer.ReadRune], UnreadRune returns an error. (In this regard
// it is stricter than [Buffer.UnreadByte], which will unread the last byte
// from any read operation.)
func (b *Buffer) UnreadRune() error {
if b.lastRead <= opInvalid {
return errors.New("bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune")
}
if b.off >= int(b.lastRead) {
b.off -= int(b.lastRead)
}
b.lastRead = opInvalid
return nil
}
var errUnreadByte = errors.New("bytes.Buffer: UnreadByte: previous operation was not a successful read")
// UnreadByte unreads the last byte returned by the most recent successful
// read operation that read at least one byte. If a write has happened since
// the last read, if the last read returned an error, or if the read read zero
// bytes, UnreadByte returns an error.
func (b *Buffer) UnreadByte() error {
if b.lastRead == opInvalid {
return errUnreadByte
}
b.lastRead = opInvalid
if b.off > 0 {
b.off--
}
return nil
}
// ReadBytes reads until the first occurrence of delim in the input,
// returning a slice containing the data up to and including the delimiter.View on GitHub (pinned to b6b368adc5)