{"record":{"id":"440ac02a6034a34e","repo":"gofr-dev/gofr","slug":"w-got-d","errorCode":null,"errorMessage":"%w (got: %d)","messagePattern":"%w \\(got: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/gofr/datasource/file/ftp/storage_adapter.go","lineNumber":121,"sourceCode":"\tif err != nil {\n\t\tif isFTPNotFoundError(err) {\n\t\t\treturn nil, fmt.Errorf(\"%w %q: %w\", errObjectNotFound, name, err)\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"%w for %q: %w\", errFailedToCreateReader, name, err)\n\t}\n\n\treturn reader, nil\n}\n\n// NewRangeReader creates a range reader for the given object.\nfunc (s *storageAdapter) NewRangeReader(_ context.Context, name string, offset, length int64) (io.ReadCloser, error) {\n\tif name == \"\" {\n\t\treturn nil, errEmptyObjectName\n\t}\n\n\tif offset < 0 {\n\t\treturn nil, fmt.Errorf(\"%w (got: %d)\", errInvalidOffset, offset)\n\t}\n\n\tif s.conn == nil {\n\t\treturn nil, errFTPClientNotInitialized\n\t}\n\n\tobjectPath := s.buildPath(name)\n\n\treader, err := s.conn.RetrFrom(objectPath, uint64(offset))\n\tif err != nil {\n\t\tif isFTPNotFoundError(err) {\n\t\t\treturn nil, fmt.Errorf(\"%w %q: %w\", errObjectNotFound, name, err)\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"%w for %q at offset %d: %w\", errFailedToCreateReader, name, offset, err)\n\t}\n\n\t// FTP doesn't support length limit in RetrFrom, so wrap in LimitReader","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/gofr-dev/gofr/blob/187eb24962502e91f1fee856230670958b66e89c/pkg/gofr/datasource/file/ftp/storage_adapter.go#L103-L139","documentation":"NewRangeReader rejects a negative offset because FTP byte ranges must be non-negative. The library returns errInvalidOffset including the offending value via \"(got: %d)\". This is a pure caller-input validation error raised before any network I/O.","triggerScenarios":"Calling storageAdapter.NewRangeReader(ctx, name, offset, length) with offset < 0 (e.g. -1 used as a sentinel for 'read from end' or an uninitialized int64).","commonSituations":"Passing -1 to mean 'tail of file' (not supported here), arithmetic underflow when computing offsets from sizes, uninitialized offset variables.","solutions":["Clamp the offset to 0 before calling: if offset < 0 { offset = 0 }","For tail reads, first StatObject to get the size and compute offset = size - n","Check the caller's offset computation for underflow (e.g. size - n when n > size)"],"exampleFix":"// before\nr, err := store.NewRangeReader(ctx, \"log.txt\", -1024, 1024)\n// after\ninfo, _ := store.StatObject(ctx, \"log.txt\")\noffset := info.Size - 1024\nif offset < 0 { offset = 0 }\nr, err := store.NewRangeReader(ctx, \"log.txt\", offset, 1024)","handlingStrategy":"validation","validationCode":"if offset < 0 {\n    return errors.New(\"offset must be >= 0\")\n}\nif length < 0 {\n    length = 0 // read to EOF\n}","typeGuard":null,"tryCatchPattern":"r, err := store.NewRangeReader(ctx, name, offset, length)\nif err != nil && strings.Contains(err.Error(), \"invalid offset\") {\n    offset = 0\n    r, err = store.NewRangeReader(ctx, name, offset, length)\n}","preventionTips":["Clamp computed offsets (size-n) to 0 to avoid underflow","Never use -1 as a sentinel for 'read from end'; StatObject first","Validate offset/length at your API boundary before calling the adapter"],"tags":["validation","ftp","range-request"],"backgroundTag":"invalid-range-offset","analyzedSha":"187eb24962502e91f1fee856230670958b66e89c","analyzedAt":"2026-09-01T20:34:54.554Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}