{"record":{"id":"835a7eb7caad5e00","repo":"juicedata/juicefs","slug":"failed-to-get-the-first-byte-expect-h-but-got","errorCode":null,"errorMessage":"failed to get the first byte:, expect \"h\", but got %q, error: %s","messagePattern":"failed to get the first byte:, expect \"h\", but got %q, error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/objbench.go","lineNumber":817,"sourceCode":"\t})\n\n\trunCase(\"get non-exist\", func(blob object.ObjectStorage) error {\n\t\tif _, err := blob.Get(ctx, \"not_exists_file\", 0, -1); err == nil {\n\t\t\treturn fmt.Errorf(\"get not existed object should failed: %s\", err)\n\t\t}\n\t\treturn nil\n\t})\n\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","sourceCodeStart":799,"sourceCodeEnd":835,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/cmd/objbench.go#L799-L835","documentation":"This error is produced by the 'get partial object' benchmark case in `juicefs objbench` (cmd/objbench.go). The benchmark stores the object \"hello\" (5 bytes) and then issues a ranged GET of 1 byte at offset 0 via the object-storage abstraction (object.ObjectStorage.Get). If the returned data is not exactly \"h\" or the call returns an error, the case fails with this message. It signals that the backend does not honor partial (range) reads at the start of an object.","triggerScenarios":"Running `juicefs objbench` against an object storage whose Get(key, off=0, size=1) returns the wrong bytes (e.g. the whole object, empty data, or corrupted content) or returns a non-nil error such as 416/timeout/auth failure.","commonSituations":"Testing a custom or third-party ObjectStorage implementation that ignores the offset/limit parameters; an S3-compatible endpoint that mishandles Range headers; proxy/gateway layers that rewrite or truncate ranged GETs; stale or eventually-consistent reads right after Put.","solutions":["Inspect the error suffix (%s) to see the underlying storage error and fix that first (credentials, network, endpoint).","If data is wrong, verify the backend honors Range requests: `aws s3api get-object --range bytes=0-0` against the same endpoint.","Update or fix the custom object.ObjectStorage implementation so Get(off, size) slices correctly.","Re-run only this case with `juicefs objbench --small-color=... `/ relevant flags or use `juicefs gc`-free single-case testing to confirm."],"exampleFix":"// before (custom storage ignoring range)\nfunc (s *myStore) Get(ctx, key string, off, limit int64) (io.ReadCloser, error) {\n    return s.get(key, 0, -1) // returns whole object, d == \"hello\"\n}\n// after\nfunc (s *myStore) Get(ctx, key string, off, limit int64) (io.ReadCloser, error) {\n    return s.getRange(key, off, limit) // d == \"h\"\n}","handlingStrategy":"validation","validationCode":"// before running objbench against a backend, smoke-test ranged reads:\nrc, err := blob.Get(ctx, key, 0, 1)\nif err != nil { log.Fatalf(\"ranged get failed: %v\", err) }\nbuf, _ := io.ReadAll(rc); rc.Close()\nif len(buf) != 1 || buf[0] != 'h' { log.Fatalf(\"bad range read: %q\", buf) }","typeGuard":"func isOneByte(d string, err error) bool { return err == nil && len(d) == 1 }","tryCatchPattern":"if d, e := get(blob, key, 0, 1); e != nil || d != \"h\" {\n    log.Printf(\"partial get (first byte) failed: data=%q err=%v\", d, e)\n    // fall back to full-object read + slice\n}","preventionTips":["Smoke-test Range requests (bytes=0-0) against the endpoint before benchmarking.","Always check both the returned data and the error, as objbench does.","Test custom ObjectStorage implementations against objbench before production use.","Watch for eventual-consistency windows between Put and Get in the benchmark."],"tags":["object-storage","range-read","benchmark","s3"],"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-14T05:17:10.506Z"}