{"record":{"id":"a12cd4af1a9e2eac","repo":"juicedata/juicefs","slug":"failed-to-get-object-with-the-end-out-of-range-ex","errorCode":null,"errorMessage":"failed to get object with the end out of range, expect \"o\", but got %q, error: %s","messagePattern":"failed to get object with the end out of range, expect \"o\", but got %q, error: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/objbench.go","lineNumber":833,"sourceCode":"\t\t// get first\n\t\tif d, e := get(blob, key, 0, 1); e != nil || d != \"h\" {\n\t\t\treturn fmt.Errorf(`failed to get the first byte:, expect \"h\", but got %q, error: %s`, d, e)\n\t\t}\n\t\t// get last\n\t\tif d, e := get(blob, key, 4, 1); e != nil || d != \"o\" {\n\t\t\treturn fmt.Errorf(`failed to get the last byte: expect \"o\", but got %q, error: %s`, d, e)\n\t\t}\n\t\t// get last 3\n\t\tif d, e := get(blob, key, 2, 3); e != nil || d != \"llo\" {\n\t\t\treturn fmt.Errorf(`failed to get the last three bytes: expect \"llo\", but got %q, error: %s`, d, e)\n\t\t}\n\t\t// get middle\n\t\tif d, e := get(blob, key, 2, 2); e != nil || d != \"ll\" {\n\t\t\treturn fmt.Errorf(`failed to get two bytes: expect \"ll\", but got %q, error: %s`, d, e)\n\t\t}\n\t\t// get the end out of range\n\t\tif d, e := get(blob, key, 4, 2); e != nil || d != \"o\" {\n\t\t\treturn warning(fmt.Errorf(`failed to get object with the end out of range, expect \"o\", but got %q, error: %s`, d, e))\n\t\t}\n\t\t// get the off out of range\n\t\tif d, e := get(blob, key, 6, 2); e != nil || d != \"\" {\n\t\t\treturn warning(fmt.Errorf(`failed to get object with the offset out of range, expect \"\", but got %q, error: %s`, d, e))\n\t\t}\n\t\treturn nil\n\t})\n\n\trunCase(\"head an object\", func(blob object.ObjectStorage) error {\n\t\tbr := []byte(\"hello\")\n\t\tif err := blob.Put(ctx, key, bytes.NewReader(br)); err != nil {\n\t\t\treturn fmt.Errorf(\"put object failed: %s\", err)\n\t\t}\n\t\tdefer blob.Delete(ctx, key) //nolint:errcheck\n\t\tif h, err := blob.Head(ctx, key); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to head object %s\", err)\n\t\t} else {\n\t\t\tif h.Key() != key {","sourceCodeStart":815,"sourceCodeEnd":851,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/cmd/objbench.go#L815-L851","documentation":"Emitted by the 'get partial object' case of `juicefs objbench` when a read whose requested range extends past the end of the object (offset 4, size 2 on a 5-byte object) does not return just the remaining byte \"o\". Unlike the other partial-get checks this is wrapped in `warning()`, so it is reported as a warning rather than a hard failure: per the S3 spec, ranges exceeding object length should be truncated, but some backends legitimately return an error. It flags backends that neither truncate the range nor (if erroring) behave as the benchmark expects.","triggerScenarios":"ObjectStorage.Get(key, off=4, size=2) against a 5-byte object: returns wrong bytes, the full tail plus padding, or errors (e.g. HTTP 416 InvalidRange) instead of returning the truncated \"o\".","commonSituations":"S3-compatible endpoints that return 416 for overlong ranges instead of clamping; NAS/HDFS-backed stores with strict bounds checks; custom implementations with different out-of-range semantics; benchmarking a new backend to document its range behavior.","solutions":["Determine which behavior the backend has: truncation (correct per S3) or 416 (tolerable, warning-level).","If the backend returns 416 for over-range reads, treat this warning as expected or adjust the storage layer to clamp ranges to object size.","If bytes are wrong/garbled, fix the tail-range slicing in the ObjectStorage implementation.","Check the appended %s error to distinguish an HTTP status problem from a data problem."],"exampleFix":"// before (strict bounds -> 416)\nif off+limit > size { return nil, ErrOutOfRange }\n// after (clamp to object size, S3 semantics)\nif limit < 0 || off+limit > size { limit = size - off }","handlingStrategy":"fallback","validationCode":"// clamp ranges to object size before calling Get:\nh, _ := blob.Head(ctx, key)\nsize := h.Size\nif off >= size { /* skip or expect empty */ }\nif limit > 0 && off+limit > size { limit = size - off }","typeGuard":null,"tryCatchPattern":"d, e := get(blob, key, 4, 2)\nif e != nil || d != \"o\" {\n    // tolerate 416/InvalidRange backends, but log for review\n    log.Printf(\"out-of-range read: got=%q err=%v\", d, e)\n    // fallback: Head for size, then re-read clamped range\n}","preventionTips":["Implement S3 range semantics (truncate over-long ranges) in custom stores.","Treat this as a warning-level check; document backends that return 416 instead.","Head the object to get its size and clamp client-side ranges before Get."],"tags":["object-storage","range-read","out-of-range","benchmark"],"backgroundTag":"value-out-of-range","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}