{"record":{"id":"fd36c8f401d05450","repo":"juicedata/juicefs","slug":"failed-to-get-the-last-three-bytes-expect-llo","errorCode":null,"errorMessage":"failed to get the last three bytes: expect \"llo\", but got %q, error: %s","messagePattern":"failed to get the last three bytes: expect \"llo\", but got %q, error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/objbench.go","lineNumber":825,"sourceCode":"\n\trunCase(\"get partial 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\n\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\")","sourceCodeStart":807,"sourceCodeEnd":843,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/cmd/objbench.go#L807-L843","documentation":"Emitted by the 'get partial object' case of `juicefs objbench` when a middle-to-end ranged read (offset 2, size 3) of the 5-byte \"hello\" object does not return \"llo\". This validates multi-byte range reads that end exactly at the object's last byte. A failure means the backend's offset+limit slicing or Range request translation is wrong, or the read itself errored.","triggerScenarios":"ObjectStorage.Get(key, off=2, size=3) returns data other than \"llo\", returns the full object, returns fewer bytes, or returns an error (HTTP 416, truncation, network failure).","commonSituations":"Custom storage layers that pass an exclusive end as inclusive (or vice versa) to the underlying API; S3 gateways mangling `bytes=2-4`; backends that clamp size incorrectly near EOF; cached/stale objects of different length.","solutions":["Read the appended error (%s) for the underlying storage failure.","Verify with a direct ranged request (`--range bytes=2-4`) against the backend.","Fix end-offset computation in the ObjectStorage implementation (inclusive vs exclusive end).","Re-run objbench to confirm all partial-get cases pass together."],"exampleFix":"// before\nbuf := data[off : off+limit+1] // returns \"llo\\0\" or panics\n// after\nbuf := data[off : off+limit] // returns \"llo\"","handlingStrategy":"validation","validationCode":"// check the backend returns exactly the requested slice:\nrc, err := blob.Get(ctx, key, 2, 3)\nif err != nil { return err }\nbuf, _ := io.ReadAll(rc); rc.Close()\nif string(buf) != \"llo\" { return fmt.Errorf(\"range 2+3 got %q\", buf) }","typeGuard":"func isExactRange(d string, want string, err error) bool { return err == nil && d == want }","tryCatchPattern":"d, e := get(blob, key, 2, 3)\nif e != nil || d != \"llo\" {\n    log.Printf(\"range read mismatch: got=%q err=%v\", d, e)\n    // fallback: full read + local slice\n}","preventionTips":["Unit-test offset+limit slicing (interior and edge ranges) in custom stores.","Keep a single helper for range math to avoid inclusive/exclusive mistakes.","Re-run the full objbench get case group after fixing one range bug."],"tags":["object-storage","range-read","benchmark"],"backgroundTag":"unexpected-response-shape","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"}