{"record":{"id":"e614c262f68b38e0","repo":"juicedata/juicefs","slug":"list-should-return-2-keys-but-got-d","errorCode":null,"errorMessage":"list should return 2 keys, but got %d","messagePattern":"list should return 2 keys, but got (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/objbench.go","lineNumber":893,"sourceCode":"\n\trunCase(\"delete non-exist\", func(blob object.ObjectStorage) error {\n\t\tif err := blob.Delete(ctx, key); err != nil {\n\t\t\treturn fmt.Errorf(\"deleting a non-existent object returns an error %v\", err)\n\t\t}\n\t\treturn nil\n\t})\n\n\trunCase(\"list objects\", 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\t\tif isFileSystem {\n\t\t\tobjs, err := listAll(ctx, blob, \"\", \"\", 2)\n\t\t\tif err == nil {\n\t\t\t\tif len(objs) != 2 {\n\t\t\t\t\treturn fmt.Errorf(\"list should return 2 keys, but got %d\", len(objs))\n\t\t\t\t}\n\t\t\t\tif objs[0].Key() != \"\" {\n\t\t\t\t\treturn fmt.Errorf(\"first key should be empty string, but got %s\", objs[0].Key())\n\t\t\t\t}\n\t\t\t\tif objs[0].Size() != 0 {\n\t\t\t\t\treturn fmt.Errorf(\"first object size should be 0, but got %d\", objs[0].Size())\n\t\t\t\t}\n\t\t\t\tif objs[1].Key() != key {\n\t\t\t\t\treturn fmt.Errorf(\"first key should be test, but got %s\", objs[1].Key())\n\t\t\t\t}\n\t\t\t\tif objs[1].Size() != 5 {\n\t\t\t\t\treturn fmt.Errorf(\"size of first key shold be 5, but got %v\", objs[1].Size())\n\t\t\t\t}\n\t\t\t\tnow := time.Now()\n\t\t\t\tif objs[1].Mtime().Before(now.Add(-30*time.Second)) || objs[1].Mtime().After(now.Add(time.Second*30)) {\n\t\t\t\t\treturn fmt.Errorf(\"mtime of key should be within 30 seconds, but got %s\", objs[1].Mtime().Sub(now))\n\t\t\t\t}\n\t\t\t} else {","sourceCodeStart":875,"sourceCodeEnd":911,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/cmd/objbench.go#L875-L911","documentation":"Raised in the objbench 'list objects' case for filesystem-backed stores (isFileSystem): listAll(ctx, blob, \"\", \"\", 2) must return exactly 2 entries — the mandatory directory entry for \"\" (size 0) plus the test object \"test\" — but a different number of entries was returned. This verifies that filesystem-style backends emulate listing-with-directory-entries correctly.","triggerScenarios":"The test puts one object at `key` under a filesystem backend (e.g. file:// or sftp://), then lists with prefix \"\" marker \"\" limit 2 and receives != 2 objects — typically because the backend does not synthesize the empty-string directory entry, or leftover objects in the test prefix change the count.","commonSituations":"Custom filesystem-like ObjectStorage implementations that omit the \"\" directory placeholder; running against a shared/dirty directory containing other files; a limit/offset handling bug in the backend's List method.","solutions":["Confirm the test directory/prefix is clean (no other keys beginning with the test prefix).","If implementing a filesystem backend, ensure List returns the directory entry \"\" as the first object with size 0.","Verify the backend honors the limit parameter the same way listAll accumulates pages.","Check JuiceFS version — list semantics for file backends changed historically; upgrade to a consistent version.","Rerun objbench on an empty temp directory to isolate."],"exampleFix":"// before (custom fs backend list omits dir entry)\nobjs := []object.Object{fileObj}\n// after\nobjs := []object.Object{&dirEntry{key: \"\", size: 0}, fileObj} // include \"\" entry first","handlingStrategy":"type-guard","validationCode":"objs, err := listAll(ctx, blob, \"\", \"\", 2)\nif err == nil && len(objs) != 2 {\n    log.Printf(\"unexpected list length: %d\", len(objs))\n}","typeGuard":"func hasDirEntry(objs []object.Object) bool {\n    return len(objs) > 0 && objs[0].Key() == \"\" && objs[0].Size() == 0\n}","tryCatchPattern":"objs, err := listAll(ctx, blob, \"\", \"\", 2)\nif err != nil {\n    return err\n}\nif !hasDirEntry(objs) {\n    log.Printf(\"backend does not synthesize directory entry; not filesystem-conformant\")\n}","preventionTips":["Keep the benchmark directory clean of foreign keys.","Implement List per the reference file:// backend including the \"\" entry.","Respect the limit parameter when paging.","Run objbench on an empty temp dir first."],"tags":["object-storage","list","filesystem","benchmark"],"backgroundTag":"unexpected-api-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"}