{"record":{"id":"ac1369e6291c271b","repo":"gofr-dev/gofr","slug":"w-negative-offset-d","errorCode":null,"errorMessage":"%w: negative offset %d","messagePattern":"%w: negative offset (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/datasource/file/s3/file.go","lineNumber":207,"sourceCode":"func (f *S3File) ReadAt(p []byte, offset int64) (n int, err error) {\n\tbucketName := getBucketName(f.name)\n\tfileName := f.relativeKey()\n\n\tvar msg string\n\n\tst := statusErr\n\n\tdefer f.sendOperationStats(&FileLog{\n\t\tOperation: \"READAT\",\n\t\tLocation:  getLocation(bucketName),\n\t\tStatus:    &st,\n\t\tMessage:   &msg,\n\t}, time.Now())\n\n\t// A negative offset is never valid. Reject it before spending a request.\n\tif offset < 0 {\n\t\tmsg = fmt.Sprintf(\"Negative offset %v\", offset)\n\t\treturn 0, fmt.Errorf(\"%w: negative offset %d\", ErrOutOfRange, offset)\n\t}\n\n\t// An empty destination reads nothing; return without a request.\n\tif len(p) == 0 {\n\t\tst = statusSuccess\n\t\treturn 0, nil\n\t}\n\n\t// A read that starts at or past EOF has nothing to return. Guard here so it\n\t// costs no request and, importantly, so io.ReaderAt callers see io.EOF rather\n\t// than an unsatisfiable Range (bytes=<size>-...) turning into an HTTP 416.\n\t// A read that merely straddles EOF (offset < size <= offset+len(p)) is valid:\n\t// S3 returns the available bytes and io.ReadFull below surfaces the short fill\n\t// as io.EOF along with the real count, honoring the io.ReaderAt contract.\n\tif offset >= f.size {\n\t\tst = statusSuccess\n\t\treturn 0, io.EOF\n\t}","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/datasource/file/s3/file.go#L189-L225","documentation":"ReadAt wraps ErrOutOfRange with the offending value when given a negative offset, because a byte position below zero can never be valid in S3 (object keys are byte-addressed from 0). The request is rejected locally before any network call is made, so it fails fast and cheaply.","triggerScenarios":"Calling (*S3File).ReadAt(p []byte, offset) with offset < 0 — e.g. a computed offset that underflowed, or passing -1 as a 'don't care' sentinel as some APIs allow.","commonSituations":"Integer arithmetic bugs producing negative positions (size subtraction going negative); porting code from APIs where -1 means 'read from current position'; unvalidated user-supplied offsets.","solutions":["Validate the offset before calling: if offset < 0 { return/adjust }.","Fix offset computation so it cannot go negative (clamp to 0 or use unsigned/checked math).","Use Seek/Read for relative positioning instead of negative ReadAt offsets.","Handle errors.Is(err, s3.ErrOutOfRange) to distinguish caller bugs from I/O errors."],"exampleFix":"// before\nn, err := f.ReadAt(buf, -1)\n// after\nif offset < 0 { return fmt.Errorf(\"invalid offset %d\", offset) }\nn, err := f.ReadAt(buf, offset)","handlingStrategy":"validation","validationCode":"func safeReadAt(f *s3datasource.S3File, p []byte, off int64) (int, error) {\n    if off < 0 {\n        return 0, fmt.Errorf(\"offset must be >= 0, got %d\", off)\n    }\n    return f.ReadAt(p, off)\n}","typeGuard":"func validOffset(off int64) bool { return off >= 0 }","tryCatchPattern":null,"preventionTips":["Validate offsets at API boundaries before calling ReadAt.","Clamp computed offsets: if off < 0 { off = 0 }.","Never use -1 as a sentinel with ReadAt (unlike some OS APIs).","Use checked/unsigned math for offset arithmetic."],"tags":["s3","validation","out-of-range","gofr"],"backgroundTag":"invalid-offset","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}