golang/go · error
bytes.Reader.UnreadRune: at beginning of slice
Error message
bytes.Reader.UnreadRune: at beginning of slice
What it means
bytes.Reader.UnreadRune returns this error when the current position r.i is 0 (reader.go:104-106). Even if a ReadRune occurred earlier, the Reader's position-based check fires first: with no position to rewind to, the unread cannot proceed. This is distinct from the 'previous operation was not ReadRune' check, which fires when position > 0 but prevRune < 0.
Source
Thrown at src/bytes/reader.go:105
func (r *Reader) ReadRune() (ch rune, size int, err error) {
if r.i >= int64(len(r.s)) {
r.prevRune = -1
return 0, 0, io.EOF
}
r.prevRune = int(r.i)
if c := r.s[r.i]; c < utf8.RuneSelf {
r.i++
return rune(c), 1, nil
}
ch, size = utf8.DecodeRune(r.s[r.i:])
r.i += int64(size)
return
}
// UnreadRune complements [Reader.ReadRune] in implementing the [io.RuneScanner] interface.
func (r *Reader) UnreadRune() error {
if r.i <= 0 {
return errors.New("bytes.Reader.UnreadRune: at beginning of slice")
}
if r.prevRune < 0 {
return errors.New("bytes.Reader.UnreadRune: previous operation was not ReadRune")
}
r.i = int64(r.prevRune)
r.prevRune = -1
return nil
}
// Seek implements the [io.Seeker] interface.
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
r.prevRune = -1
var abs int64
switch whence {
case io.SeekStart:
abs = offset
case io.SeekCurrent:
abs = r.i + offsetView on GitHub (pinned to b6b368adc5)
Solutions
- Confirm the reader is not at position 0 before calling UnreadRune (check r.Len() < r.Size() or track read state).
- Ensure a successful ReadRune precedes each UnreadRune with no intervening Seek/Reset.
- Handle the start-of-input case in the parser without attempting a pushback.
- Use ReadRune/UnreadRune in tightly paired local blocks so the invariant is obvious.
Example fix
// before — UnreadRune at position 0
r := bytes.NewReader(data)
r.UnreadRune() // error: at beginning of slice
// after — read a rune first, then unread conditionally
_, _, err := r.ReadRune()
if err == nil && pushBack {
r.UnreadRune()
} Defensive patterns
Strategy: validation
Validate before calling
// Only UnreadRune when not at the beginning and after a ReadRune.
if r.Len() < int(r.Size()) { // not at start
if err := r.UnreadRune(); err != nil { return err }
} Try / catch
if err := r.UnreadRune(); err != nil {
if strings.Contains(err.Error(), "at beginning of slice") {
// no rune to push back
} else { return err }
} Prevention
- Check the reader is not at position 0 before UnreadRune.
- Ensure ReadRune precedes UnreadRune with no intervening Seek/Reset.
- Handle start-of-input without attempting pushback.
When it happens
Trigger: Triggered by reader.UnreadRune() when r.i <= 0 (reader.go:104). Occurs at the start of the slice, after Seek to 0, after Reset, or after consuming then pushing back to the origin.
Common situations: A rune-level tokenizer that calls UnreadRune unconditionally at the top of its loop. Calling UnreadRune at the very start of input before any ReadRune. Position state corrupted by an interleaving Seek(0).
Related errors
- bytes.Reader.UnreadRune: previous operation was not ReadRune
- bytes.Reader.UnreadByte: at beginning of slice
- bytes.Buffer: UnreadRune: previous operation was not a succe
- bytes.Buffer: UnreadByte: previous operation was not a succe
- bytes.Reader.ReadAt: negative offset
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/52320f4ef77e9307.
Report an issue: GitHub.