{"record":{"id":"f004cf962072bec2","repo":"juicedata/juicefs","slug":"invalid-hscan-response-for-s-odd-number-of-eleme","errorCode":null,"errorMessage":"invalid HSCAN response for %s: odd number of elements: %d","messagePattern":"invalid HSCAN response for (.+?): odd number of elements: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/meta/redis.go","lineNumber":3978,"sourceCode":"\t\t}\n\t\tif len(keys) > 0 {\n\t\t\tif err = f(keys); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tif c == 0 {\n\t\t\tbreak\n\t\t}\n\t\tcursor = c\n\t}\n\treturn nil\n}\n\nfunc (m *redisMeta) hscanToMap(ctx context.Context, key string) (map[string]string, error) {\n\tresult := make(map[string]string)\n\terr := m.hscan(ctx, key, func(keys []string) error {\n\t\tif len(keys)%2 != 0 {\n\t\t\treturn fmt.Errorf(\"invalid HSCAN response for %s: odd number of elements: %d\", key, len(keys))\n\t\t}\n\t\tfor i := 0; i < len(keys); i += 2 {\n\t\t\tresult[keys[i]] = keys[i+1]\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\nfunc (m *redisMeta) CleanupSlices(ctx Context) syscall.Errno {\n\tlogger.Debugf(\"start cleanup...\")\n\tm.cleanupLeakedInodes(true)\n\tm.cleanupLeakedChunks(true)\n\tm.cleanupOldSliceRefs(true)\n\treturn m.baseMeta.CleanupSlices(ctx)","sourceCodeStart":3960,"sourceCodeEnd":3996,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/meta/redis.go#L3960-L3996","documentation":"hscanToMap scans a Redis hash via HSCAN and expects each batch to be flat key/value pairs. Redis guarantees an even number of elements; receiving an odd count means the response is malformed (protocol/proxy problem or a wrapped/incompatible reply), so the function refuses to build a partially-wrong map and returns this error.","triggerScenarios":"HSCAN on a hash (used by cleanup/maintenance scans over delSlices/sliceRefs-style keys) returning a keys slice whose length is not even — typically from a broken proxy, custom Redis-compatible server, or corrupted reply handling.","commonSituations":"Using Redis-compatible stores (KeyDB, Dragonfly, older Twemproxy/Codis) with non-conforming HSCAN replies; custom network middleware mangling multi-bulk replies.","solutions":["Identify the middleware/proxy between the client and Redis and test HSCAN against real Redis directly (redis-cli HSCAN key 0).","Upgrade or bypass the non-conforming proxy/compatible server.","Retry the scan; a transient truncated reply may succeed on retry.","If reproducible, capture the reply (MONITOR or tcpdump) and report to the proxy/storage vendor."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-check the backend conforms\nkeys, err := doRawHscan(testKey)\nif err != nil || len(keys)%2 != 0 { log.Fatal(\"HSCAN replies are non-conforming; check proxy/compatible server\") }","typeGuard":null,"tryCatchPattern":"m, err := hscanToMap(ctx, key)\nif err != nil && strings.Contains(err.Error(), \"invalid HSCAN response\") {\n    logger.Warnf(\"malformed HSCAN from %s, retrying once\", key)\n    m, err = hscanToMap(ctx, key)\n}","preventionTips":["Test Redis-compatible backends (KeyDB, proxies) for HSCAN conformance before production","Avoid middleware that rewrites multi-bulk replies","Pin to well-known-conformant Redis versions/proxies"],"tags":["redis","hscan","protocol","unexpected-response"],"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-14T00:17:10.932Z"}