juicedata/juicefs · error
failed to get two bytes: expect "ll", but got %q, error: %s
Error message
failed to get two bytes: expect "ll", but got %q, error: %s
What it means
Emitted by the 'get partial object' case of `juicefs objbench` when a middle ranged read (offset 2, size 2) of the "hello" object does not return exactly "ll". This checks interior (not touching either edge) partial reads. Failures indicate the backend returned extra, missing, or shifted bytes, or the Get call errored.
Source
Thrown at cmd/objbench.go:829
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")
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:errcheckView on GitHub (pinned to c9a67b23e8)
Solutions
- Check the embedded %s error for the storage-level cause.
- Confirm the backend honors limited reads (`--range bytes=2-3`).
- Fix the limit handling in the custom Get implementation so exactly `size` bytes are returned.
- Run objbench's get cases in isolation to confirm the fix.
Example fix
// before (limit ignored) r, err := s.get(key, off, -1) // after r, err := s.get(key, off, limit)
Defensive patterns
Strategy: validation
Validate before calling
rc, err := blob.Get(ctx, key, 2, 2)
if err != nil { return err }
buf, _ := io.ReadAll(rc); rc.Close()
if len(buf) != 2 { return fmt.Errorf("expected 2 bytes, got %d", len(buf)) } Try / catch
d, e := get(blob, key, 2, 2)
if e != nil || d != "ll" {
log.Printf("interior range read failed: got=%q err=%v", d, e)
} Prevention
- Assert the returned byte count matches the requested limit in tests.
- Make sure Get implementations always cap output at `limit` bytes.
- Benchmark new backends with objbench before wiring them into JuiceFS.
When it happens
Trigger: ObjectStorage.Get(key, off=2, size=2) returns more than 2 bytes (e.g. "llo" or the whole object), fewer bytes, misaligned bytes, or a non-nil error.
Common situations: Custom ObjectStorage that ignores `limit` and reads to EOF; proxies buffering the full object; range translation bugs in S3-compatible endpoints; double-counted offsets after prior failed reads.
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 the last three bytes: expect "llo", but got %q
- 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/0427b675655e7437.
Report an issue: GitHub.