{"record":{"id":"cba44b0a4458bf1f","repo":"redis/go-redis","slug":"redis-can-t-parse-array-set-push-reply-100q","errorCode":null,"errorMessage":"redis: can't parse array/set/push reply: %.100q","messagePattern":"redis: can't parse array/set/push reply: %\\.100q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/proto/reader.go","lineNumber":731,"sourceCode":"\t\treturn err\n\t}\n\tif n != fixedLen {\n\t\treturn fmt.Errorf(\"redis: got %d elements in the array, wanted %d\", n, fixedLen)\n\t}\n\treturn nil\n}\n\n// ReadArrayLen Read and return the length of the array.\nfunc (r *Reader) ReadArrayLen() (int, error) {\n\tline, err := r.ReadLine()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tswitch line[0] {\n\tcase RespArray, RespSet, RespPush:\n\t\treturn replyLen(line)\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"redis: can't parse array/set/push reply: %.100q\", line)\n\t}\n}\n\n// ReadFixedMapLen reads fixed map length.\nfunc (r *Reader) ReadFixedMapLen(fixedLen int) error {\n\tn, err := r.ReadMapLen()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif n != fixedLen {\n\t\treturn fmt.Errorf(\"redis: got %d elements in the map, wanted %d\", n, fixedLen)\n\t}\n\treturn nil\n}\n\n// ReadMapLen reads the length of the map type.\n// If responding to the array type (RespArray/RespSet/RespPush),\n// it must be a multiple of 2 and return n/2.","sourceCodeStart":713,"sourceCodeEnd":749,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/internal/proto/reader.go#L713-L749","documentation":"ReadArrayLen expected the next reply to be an array, set, or push type (RESP type bytes *, ~, >) but got something else. The parser cannot return a length for a non-aggregate reply, so it fails with the raw line for diagnosis. In RESP2 mode maps are also accepted via the array fallback, so this error means the reply is fundamentally not an aggregate.","triggerScenarios":"readReply or stream/xinfo parsers (readXMessageSlice, readXMessage, readStreamGroups, readXInfoStream*) encountering a scalar, error, or nil reply where an array was expected — e.g. an error reply (like NOSCRIPT or NOGROUP) reaching the array parser due to desync, or a nil bulk reply.","commonSituations":"Calling XREADGROUP with an invalid group so the error surfaces mid-parse; RESP2/RESP3 protocol mismatch changing reply types; connection reuse after a partially consumed reply.","solutions":["Check the redis error actually returned (NOGROUP, WRONGTYPE, etc.) — handle command-level errors before parsing","Set Protocol: 3 if server replies use RESP3 sets/pushes your parser expects as aggregates","Verify each command's reply is fully consumed to avoid stream desync","Run the command with redis-cli --raw to see the actual reply type"],"exampleFix":"// before: blindly parsing next reply as array\nn, err := reader.ReadArrayLen()\n// after: check cmd error first\nif err := cmd.Err(); err != nil { return err }\nn, err := reader.ReadArrayLen()","handlingStrategy":"try-catch","validationCode":"// ensure the key/command can return an aggregate\n// e.g. TYPE mykey == list/stream before XINFO/XRANGE-style reads","typeGuard":"func isArrayLikeReply(t byte) bool {\n  return t == '*' || t == '~' || t == '>' || t == '%'\n}","tryCatchPattern":"n, err := reader.ReadArrayLen()\nif err != nil {\n  if strings.Contains(err.Error(), \"can't parse array/set/push\") {\n    // server returned an error or scalar; surface cmd error to caller\n    return cmd.Err()\n  }\n  return err\n}","preventionTips":["Handle command errors (NOGROUP, WRONGTYPE) before parsing replies","Match RESP2/RESP3 Protocol option to the server","Consume replies fully to prevent desync","Verify reply type with redis-cli --raw for new commands"],"tags":["go-redis","resp-protocol","array","parsing"],"backgroundTag":"resp-reply-parse-failure","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}