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:errcheck

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Check the embedded %s error for the storage-level cause.
  2. Confirm the backend honors limited reads (`--range bytes=2-3`).
  3. Fix the limit handling in the custom Get implementation so exactly `size` bytes are returned.
  4. 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

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


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/0427b675655e7437. Report an issue: GitHub.