juicedata/juicefs · error
failed to get the last three bytes: expect "llo", but got %q
Error message
failed to get the last three bytes: expect "llo", but got %q, error: %s
What it means
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.
Source
Thrown at cmd/objbench.go:825
runCase("get partial object", func(blob object.ObjectStorage) error {
br := []byte("hello")
if err := blob.Put(ctx, key, bytes.NewReader(br)); err != nil {
return fmt.Errorf("put object failed: %s", err)
}
defer blob.Delete(ctx, key) //nolint:errcheck
// get first
if d, e := get(blob, key, 0, 1); e != nil || d != "h" {
return fmt.Errorf(`failed to get the first byte:, expect "h", but got %q, error: %s`, d, e)
}
// get last
if d, e := get(blob, key, 4, 1); e != nil || d != "o" {
return fmt.Errorf(`failed to get the last byte: expect "o", but got %q, error: %s`, d, e)
}
// get last 3
if d, e := get(blob, key, 2, 3); e != nil || d != "llo" {
return fmt.Errorf(`failed to get the last three bytes: expect "llo", but got %q, error: %s`, d, e)
}
// get middle
if d, e := get(blob, key, 2, 2); e != nil || d != "ll" {
return fmt.Errorf(`failed to get two bytes: expect "ll", but got %q, error: %s`, d, e)
}
// get the end out of range
if d, e := get(blob, key, 4, 2); e != nil || d != "o" {
return warning(fmt.Errorf(`failed to get object with the end out of range, expect "o", but got %q, error: %s`, d, e))
}
// get the off out of range
if d, e := get(blob, key, 6, 2); e != nil || d != "" {
return warning(fmt.Errorf(`failed to get object with the offset out of range, expect "", but got %q, error: %s`, d, e))
}
return nil
})
runCase("head an object", func(blob object.ObjectStorage) error {
br := []byte("hello")View on GitHub (pinned to c9a67b23e8)
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.
Example fix
// before buf := data[off : off+limit+1] // returns "llo\0" or panics // after buf := data[off : off+limit] // returns "llo"
Defensive patterns
Strategy: validation
Validate before calling
// check the backend returns exactly the requested slice:
rc, err := blob.Get(ctx, key, 2, 3)
if err != nil { return err }
buf, _ := io.ReadAll(rc); rc.Close()
if string(buf) != "llo" { return fmt.Errorf("range 2+3 got %q", buf) } Type guard
func isExactRange(d string, want string, err error) bool { return err == nil && d == want } Try / catch
d, e := get(blob, key, 2, 3)
if e != nil || d != "llo" {
log.Printf("range read mismatch: got=%q err=%v", d, e)
// fallback: full read + local slice
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- failed to get the first byte:, expect "h", but got %q, error
- failed to get the last byte: expect "o", but got %q, error:
- failed to get two bytes: expect "ll", but got %q, error: %s
- failed to get object with the end out of range, expect "o",
- ErrNotSUP
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/fd36c8f401d05450.
Report an issue: GitHub.