{"record":{"id":"226344a10c0717b3","repo":"golang/go","slug":"bytes-buffer-unreadrune-previous-operation-was-n","errorCode":null,"errorMessage":"bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune","messagePattern":"bytes\\.Buffer: UnreadRune: previous operation was not a successful ReadRune","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/bytes/buffer.go","lineNumber":416,"sourceCode":"\tif c < utf8.RuneSelf {\n\t\tb.off++\n\t\tb.lastRead = opReadRune1\n\t\treturn rune(c), 1, nil\n\t}\n\tr, n := utf8.DecodeRune(b.buf[b.off:])\n\tb.off += n\n\tb.lastRead = readOp(n)\n\treturn r, n, nil\n}\n\n// UnreadRune unreads the last rune returned by [Buffer.ReadRune].\n// If the most recent read or write operation on the buffer was\n// not a successful [Buffer.ReadRune], UnreadRune returns an error.  (In this regard\n// it is stricter than [Buffer.UnreadByte], which will unread the last byte\n// from any read operation.)\nfunc (b *Buffer) UnreadRune() error {\n\tif b.lastRead <= opInvalid {\n\t\treturn errors.New(\"bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune\")\n\t}\n\tif b.off >= int(b.lastRead) {\n\t\tb.off -= int(b.lastRead)\n\t}\n\tb.lastRead = opInvalid\n\treturn nil\n}\n\nvar errUnreadByte = errors.New(\"bytes.Buffer: UnreadByte: previous operation was not a successful read\")\n\n// UnreadByte unreads the last byte returned by the most recent successful\n// read operation that read at least one byte. If a write has happened since\n// the last read, if the last read returned an error, or if the read read zero\n// bytes, UnreadByte returns an error.\nfunc (b *Buffer) UnreadByte() error {\n\tif b.lastRead == opInvalid {\n\t\treturn errUnreadByte\n\t}","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/bytes/buffer.go#L398-L434","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before — UnreadRune after ReadByte fails\nb, _ := buf.ReadByte()\nbuf.UnreadRune() // error: previous op was not ReadRune\n\n// after — use ReadRune so UnreadRune is valid, or use UnreadByte\nr, _, _ := buf.ReadRune()\nif needUnread { buf.UnreadRune(); return }\n// or, for byte-level pushback:\nb, _ := buf.ReadByte()\nif needUnread { buf.UnreadByte() }","handlingStrategy":"validation","validationCode":"// Track the last op type so UnreadRune is only called after ReadRune.\ntype runeReader struct{ b *bytes.Buffer; lastWasReadRune bool }\nfunc (r *runeReader) ReadRune() (ch rune, size int, err error) {\n    ch, size, err = r.b.ReadRune()\n    r.lastWasReadRune = (err == nil)\n    return\n}\nfunc (r *runeReader) UnreadRune() error {\n    if !r.lastWasReadRune {\n        return errors.New(\"cannot unread: last op was not ReadRune\")\n    }\n    err := r.b.UnreadRune()\n    r.lastWasReadRune = false\n    return err\n}","typeGuard":null,"tryCatchPattern":"if err := buf.UnreadRune(); err != nil {\n    // err is the documented 'previous operation was not a successful ReadRune'\n    // handle by re-reading or skipping the unread\n}","preventionTips":["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."],"tags":["go","bytes","buffer","unread","validation"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}