{"record":{"id":"79232ba53d10b15a","repo":"golang/go","slug":"bytes-reader-readat-negative-offset","errorCode":null,"errorMessage":"bytes.Reader.ReadAt: negative offset","messagePattern":"bytes\\.Reader\\.ReadAt: negative offset","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/bytes/reader.go","lineNumber":53,"sourceCode":"// The result is unaffected by any method calls except [Reader.Reset].\nfunc (r *Reader) Size() int64 { return int64(len(r.s)) }\n\n// Read implements the [io.Reader] interface.\nfunc (r *Reader) Read(b []byte) (n int, err error) {\n\tif r.i >= int64(len(r.s)) {\n\t\treturn 0, io.EOF\n\t}\n\tr.prevRune = -1\n\tn = copy(b, r.s[r.i:])\n\tr.i += int64(n)\n\treturn\n}\n\n// ReadAt implements the [io.ReaderAt] interface.\nfunc (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {\n\t// cannot modify state - see io.ReaderAt\n\tif off < 0 {\n\t\treturn 0, errors.New(\"bytes.Reader.ReadAt: negative offset\")\n\t}\n\tif off >= int64(len(r.s)) {\n\t\treturn 0, io.EOF\n\t}\n\tn = copy(b, r.s[off:])\n\tif n < len(b) {\n\t\terr = io.EOF\n\t}\n\treturn\n}\n\n// ReadByte implements the [io.ByteReader] interface.\nfunc (r *Reader) ReadByte() (byte, error) {\n\tr.prevRune = -1\n\tif r.i >= int64(len(r.s)) {\n\t\treturn 0, io.EOF\n\t}\n\tb := r.s[r.i]","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/bytes/reader.go#L35-L71","documentation":"bytes.Reader.ReadAt returns this error when the caller passes a negative offset (reader.go:52-54). ReadAt must not modify reader state, and a negative offset has no meaningful position; the Reader rejects it rather than reading from an undefined location. Unlike EOF (returned when off >= len(s)), this is a programmer error, not an end-of-data signal.","triggerScenarios":"Triggered by reader.ReadAt(buf, off) where off < 0 (reader.go:52). Common when off is computed from a subtraction that underflows, or when an upstream caller passes through an unchecked negative value.","commonSituations":"Range requests with negative start offsets (e.g., HTTP Range parsing bugs). Offset arithmetic like off = start - prefix where start < prefix. Parsers that interpret a signed 'seek backward' count as an absolute offset without clamping.","solutions":["Validate off >= 0 before calling ReadAt; if a relative seek is intended, compute the absolute offset and clamp at 0.","If off is derived from user input, sanitize it (max(0, off)) and surface a clear error to the caller.","Switch to Reader.Seek + Read if you need relative positioning semantics instead of absolute ReadAt.","Add an assertion/test that the offset passed to ReadAt is non-negative for all code paths."],"exampleFix":"// before — off can be negative from subtraction\noff := start - headerLen // negative if start < headerLen\nn, err := r.ReadAt(buf, off)\n\n// after — validate and clamp\nif off < 0 {\n    return 0, fmt.Errorf(\"invalid offset %d\", off)\n}\nn, err := r.ReadAt(buf, off)","handlingStrategy":"validation","validationCode":"// Validate offset before calling ReadAt.\nfunc safeReadAt(r *bytes.Reader, p []byte, off int64) (int, error) {\n    if off < 0 {\n        return 0, fmt.Errorf(\"readat: negative offset %d\", off)\n    }\n    return r.ReadAt(p, off)\n}","typeGuard":null,"tryCatchPattern":"n, err := r.ReadAt(p, off)\nif err != nil && strings.Contains(err.Error(), \"negative offset\") {\n    // off was negative; fix the upstream offset computation\n}","preventionTips":["Always validate off >= 0 before ReadAt.","For relative seeks, compute the absolute offset and clamp at 0.","Sanitize offsets originating from user input (Range headers, cursors)."],"tags":["go","bytes","reader","readat","validation"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}