{"record":{"id":"d9f8ab2121e0e330","repo":"redis/go-redis","slug":"redis-can-t-parse-map-reply-100q","errorCode":null,"errorMessage":"redis: can't parse map reply: %.100q","messagePattern":"redis: can't parse map reply: %\\.100q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/proto/reader.go","lineNumber":770,"sourceCode":"\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) {\n\tif len(line) == 0 {\n\t\treturn errors.New(\"redis: invalid line\")\n\t}\n\tswitch line[0] {","sourceCodeStart":752,"sourceCodeEnd":788,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/internal/proto/reader.go#L752-L788","documentation":"ReadMapLen expected a RESP3 map type byte (%) or, as fallback, an RESP2 array byte, but the reply line starts with neither. The parser cannot interpret the reply as a map and reports the raw line truncated to 100 chars. This signals a reply type mismatch for map-expecting commands.","triggerScenarios":"readReply, stringInterfaceMapParser (HGETALL-like), readStreamGroups, readXInfoStreamConsumers, readFunctions, readEngines encountering a scalar/error/nil reply where a map was expected — often a Redis error (e.g. WRONGTYPE, unknown command) surfacing at the wrong parse layer, or RESP2 server with Protocol: 3 configured.","commonSituations":"Forcing Protocol: 3 against a RESP2-only server or proxy; calling HGETALL on a non-hash key so WRONGTYPE arrives mid-parse; desync from earlier unconsumed replies.","solutions":["Set Protocol to match the server (Protocol: 3 only for RESP3-capable servers)","Handle command-level errors (WRONGTYPE, NOGROUP) before treating the reply as data","Confirm the key holds the expected type before issuing map-returning commands","Check for desync — reset pooled connections after errors or timeouts"],"exampleFix":"// before\nopt := &redis.Options{Addr: addr, Protocol: 3}\n// after: only request RESP3 when the server supports it\nopt := &redis.Options{Addr: addr, Protocol: 3} // ensure server >= 6 / proxy supports RESP3\n// or fall back:\nopt := &redis.Options{Addr: addr} // RESP2 default","handlingStrategy":"type-guard","validationCode":"// check server RESP capability before Protocol: 3\n// HELLO 3 — if it errors, keep Protocol: 2","typeGuard":"func isMapLikeReply(t byte) bool {\n  return t == '%' || t == '*' || t == '~' || t == '>'\n}","tryCatchPattern":"n, err := reader.ReadMapLen()\nif err != nil {\n  if strings.Contains(err.Error(), \"can't parse map reply\") {\n    // likely a server error reply or RESP2/3 mismatch\n    return cmd.Err()\n  }\n  return err\n}","preventionTips":["Only enable Protocol: 3 on RESP3-capable servers/proxies","Check key TYPE before map-returning commands (HGETALL on non-hash)","Handle cmd-level errors before data parsing","Reset desynced connections instead of continuing reads"],"tags":["go-redis","resp-protocol","map","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"}