{"record":{"id":"c03147905586e16b","repo":"redis/go-redis","slug":"redis-invalid-map-key-v","errorCode":null,"errorMessage":"redis: invalid map key %#v","messagePattern":"redis: invalid map key %#v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command.go","lineNumber":6365,"sourceCode":"func (cmd *MapMapStringInterfaceCmd) Val() map[string]interface{} {\n\tcmd.await()\n\treturn cmd.val\n}\n\n// readReply will try to parse the reply from the proto.Reader for both resp2 and resp3\nfunc (cmd *MapMapStringInterfaceCmd) readReply(rd *proto.Reader) (err error) {\n\tdata, err := rd.ReadReply()\n\tif err != nil {\n\t\treturn err\n\t}\n\tresultMap := map[string]interface{}{}\n\n\tswitch midResponse := data.(type) {\n\tcase map[interface{}]interface{}: // resp3 will return map\n\t\tfor k, v := range midResponse {\n\t\t\tstringKey, ok := k.(string)\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"redis: invalid map key %#v\", k)\n\t\t\t}\n\t\t\tresultMap[stringKey] = v\n\t\t}\n\tcase []interface{}: // resp2 will return array of arrays\n\t\tn := len(midResponse)\n\t\tfor i := 0; i < n; i++ {\n\t\t\tfinalArr, ok := midResponse[i].([]interface{}) // final array that we need to transform to map\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"redis: unexpected response %#v\", data)\n\t\t\t}\n\t\t\tm := len(finalArr)\n\t\t\tif m%2 != 0 { // since this should be map, keys should be even number\n\t\t\t\treturn fmt.Errorf(\"redis: unexpected response %#v\", data)\n\t\t\t}\n\n\t\t\tfor j := 0; j < m; j += 2 {\n\t\t\t\tstringKey, ok := finalArr[j].(string) // the first one\n\t\t\t\tif !ok {","sourceCodeStart":6347,"sourceCodeEnd":6383,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/command.go#L6347-L6383","documentation":"Thrown by the reply normalizer for commands like `FUNCTION DUMP`/`FCALL` consumers that flatten RESP3 maps into map[string]interface{} (command.go, case map[interface{}]interface{}). RESP3 maps may have non-string keys; this transformer only supports string keys, so any non-string key (integer, nested value, etc.) makes it abort with `redis: invalid map key`.","triggerScenarios":"Calling a command whose reply is normalized through this transformer (e.g. `FunctionList`, FT/JSON helpers that use MapStringInterfaceCmd-style parsing) when the server returns a RESP3 map whose key is not a Go string — for example a Redis version or module emitting integer-valued keys in that map.","commonSituations":"Newer server versions adding fields/keys the transformer doesn't expect; mixing RESP2/RESP3 (Protocol: 3) with a module that returns unusual map keys; connecting to Redis Enterprise or a module whose reply shape differs from OSS Redis.","solutions":["Upgrade go-redis to the latest v9 release — the transformers are updated regularly to accept new server reply shapes.","Compare the reply against a supported server version (`INFO server`); pin to a Redis version known to work with your go-redis release.","Capture the raw reply with `redis-cli --raw <command>` or a ProcessHook to see the offending key type, then file/consult a go-redis issue.","If you control the reply source (test mock), make all map keys strings."],"exampleFix":"// before: Protocol not set, server/module returns map with int keys\nopt := &redis.Options{Addr: addr}\n\n// after: pin RESP2 (array-of-arrays path) or upgrade go-redis that handles the new shape\nopt := &redis.Options{Addr: addr, Protocol: 2}","handlingStrategy":"validation","validationCode":"// verify protocol and server version before commands relying on map replies\ninfo, _ := rdb.Info(ctx, \"server\").Result()\nif !strings.Contains(info, \"redis_version:7\") && !strings.Contains(info, \"redis_version:8\") {\n    // older/unknown server: avoid map-normalized commands\n}","typeGuard":"func isStringKeyMap(m map[interface{}]interface{}) bool {\n    for k := range m {\n        if _, ok := k.(string); !ok {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"res, err := cmd.Result()\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid map key\") {\n        // dump raw reply via a ProcessHook for diagnosis, then fallback\n        return fallbackParse(raw)\n    }\n    return err\n}","preventionTips":["Keep go-redis and your Redis server versions aligned; upgrade the client when upgrading the server.","Prefer RESP3 (`Protocol: 3`) so maps arrive natively instead of through the RESP2 transformer.","Avoid Redis-compatible forks/proxies for commands with complex replies, or test them in CI.","In test mocks, always use string map keys."],"tags":["redis","protocol-parse","resp3","map-key"],"backgroundTag":"unexpected-reply-shape","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}