golang/go · error
bytes.Buffer: UnreadRune: previous operation was not a succe
Error message
bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune
What it means
bytes.Buffer.UnreadRune returns this error when the most recent operation on the buffer was not a successful ReadRune (buffer.go:416-419, guarded by b.lastRead <= opInvalid). The lastRead field is set to opReadRune1..4 only by a successful ReadRune; any other read, any write, or Reset clears it to opInvalid or opRead. UnreadRune is intentionally stricter than UnreadByte.
Source
Thrown at src/bytes/buffer.go:416
if c < utf8.RuneSelf {
b.off++
b.lastRead = opReadRune1
return rune(c), 1, nil
}
r, n := utf8.DecodeRune(b.buf[b.off:])
b.off += n
b.lastRead = readOp(n)
return r, n, nil
}
// 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
}View on GitHub (pinned to b6b368adc5)
Solutions
- Only call UnreadRune when the immediately preceding successful operation was ReadRune — track this in your parser state.
- If you need to push back after an arbitrary read, use UnreadByte (which accepts any read op) and re-decode the rune yourself.
- Restructure the read loop so ReadRune and UnreadRune are paired locally with no intervening Buffer calls.
- Test the parser against inputs that exercise the unread path to confirm the lastRead invariant holds.
Example fix
// before — UnreadRune after ReadByte fails
b, _ := buf.ReadByte()
buf.UnreadRune() // error: previous op was not ReadRune
// after — use ReadRune so UnreadRune is valid, or use UnreadByte
r, _, _ := buf.ReadRune()
if needUnread { buf.UnreadRune(); return }
// or, for byte-level pushback:
b, _ := buf.ReadByte()
if needUnread { buf.UnreadByte() } Defensive patterns
Strategy: validation
Validate before calling
// Track the last op type so UnreadRune is only called after ReadRune.
type runeReader struct{ b *bytes.Buffer; lastWasReadRune bool }
func (r *runeReader) ReadRune() (ch rune, size int, err error) {
ch, size, err = r.b.ReadRune()
r.lastWasReadRune = (err == nil)
return
}
func (r *runeReader) UnreadRune() error {
if !r.lastWasReadRune {
return errors.New("cannot unread: last op was not ReadRune")
}
err := r.b.UnreadRune()
r.lastWasReadRune = false
return err
} Try / catch
if err := buf.UnreadRune(); err != nil {
// err is the documented 'previous operation was not a successful ReadRune'
// handle by re-reading or skipping the unread
} Prevention
- Only call UnreadRune immediately after a successful ReadRune.
- Do not interleave writes, ReadByte, or Reset between ReadRune and UnreadRune.
- If you need pushback after arbitrary reads, use UnreadByte and re-decode.
When it happens
Trigger: Triggered by calling buffer.UnreadRune() when the preceding call was Read/ReadByte/ReadSlice/Write/WriteString/Reset (anything that is not a successful ReadRune), or when no read has happened yet. The check at buffer.go:417 `if b.lastRead <= opInvalid` covers all of those.
Common situations: Calling UnreadRune after a Read loop that used ReadByte or Read rather than ReadRune. Calling UnreadRune after a Write interleaved between ReadRune and UnreadRune. Defensive parsers that call UnreadRune speculatively without tracking the prior operation.
Related errors
- bytes.Buffer: UnreadByte: previous operation was not a succe
- bytes.Reader.UnreadByte: at beginning of slice
- bytes.Reader.UnreadRune: at beginning of slice
- bytes.Reader.UnreadRune: previous operation was not ReadRune
- bytes.Buffer: truncation out of range
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/226344a10c0717b3.
Report an issue: GitHub.