{"record":{"id":"e0eea2baf2409a1f","repo":"redis/go-redis","slug":"redis-the-length-of-the-array-must-be-a-multiple","errorCode":null,"errorMessage":"redis: the length of the array must be a multiple of 2, got: %d","messagePattern":"redis: the length of the array must be a multiple of 2, got: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/proto/reader.go","lineNumber":766,"sourceCode":"// If responding to the array type (RespArray/RespSet/RespPush),\n// it must be a multiple of 2 and return n/2.\n// Other types will return an error.\nfunc (r *Reader) ReadMapLen() (int, error) {\n\tline, err := r.ReadLine()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tswitch line[0] {\n\tcase RespMap:\n\t\treturn replyLen(line)\n\tcase RespArray, RespSet, RespPush:\n\t\t// Some commands and RESP2 protocol may respond to array types.\n\t\tn, err := replyLen(line)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif n%2 != 0 {\n\t\t\treturn 0, fmt.Errorf(\"redis: the length of the array must be a multiple of 2, got: %d\", n)\n\t\t}\n\t\treturn n / 2, nil\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"redis: can't parse map reply: %.100q\", line)\n\t}\n}\n\n// DiscardNext read and discard the data represented by the next line.\nfunc (r *Reader) DiscardNext() error {\n\tline, err := r.readLine()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn r.Discard(line)\n}\n\n// Discard the data represented by line.\nfunc (r *Reader) Discard(line []byte) (err error) {","sourceCodeStart":748,"sourceCodeEnd":784,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/internal/proto/reader.go#L748-L784","documentation":"When reading a map reply, the parser sometimes receives an RESP2 array (some commands and RESP2 encode maps as flat arrays). It then requires the array length to be even — each key needs a value. An odd length means the flat key/value stream is malformed, so decoding would misalign.","triggerScenarios":"ReadMapLen called (by readReply, stringInterfaceMapParser, readStreamGroups, readXInfoStreamConsumers, readFunctions, readEngines) when the reply arrives as an RESP2 array with an odd element count — a malformed or desynchronized stream, or a server/proxy producing a broken flat map encoding.","commonSituations":"Protocol desync after an earlier reply was partially read; buggy middleware rewriting HGETALL/XINFO-style replies; truncated reply from an aborted connection being reused.","solutions":["Reset the connection after any read timeout/partial read instead of reusing it — desync is the usual cause","Verify no proxy/load balancer truncates or rewrites multi-element replies","Check the command's reply shape against the server version (some commands changed arity between versions)","Re-run the failing command with redis-cli --raw and count the flat elements"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// odd flat-array length indicates desync — validate alignment upstream\n// ensure prior replies consumed: err := cmd.Err(); consume full pipeline","typeGuard":null,"tryCatchPattern":"n, err := reader.ReadMapLen()\nif err != nil && strings.Contains(err.Error(), \"multiple of 2\") {\n  logger.Error(\"map stream misaligned; dropping connection\", \"err\", err)\n  _ = conn.Close()\n  return err\n}","preventionTips":["Never reuse a pooled conn after timeout/partial read","Ensure pipelines are fully flushed and read","Check middleware for reply rewriting","Count flat elements with redis-cli --raw when diagnosing"],"tags":["go-redis","resp-protocol","map","protocol-desync"],"backgroundTag":"unexpected-reply-shape","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}