go-redis/redis · error
redis: invalid map key %#v
Error message
redis: invalid map key %#v
What it means
Thrown by MapMapStringInterfaceCmd.readReply (command.go:6359), used by FT.CONFIG GET. Under RESP3 Redis returns a real map; this parser requires every map key to be a string. If a key asserts to a non-string type, parsing aborts with the offending value.
Source
Thrown at command.go:6359
func (cmd *MapMapStringInterfaceCmd) Val() map[string]interface{} {
cmd.await()
return cmd.val
}
// readReply will try to parse the reply from the proto.Reader for both resp2 and resp3
func (cmd *MapMapStringInterfaceCmd) readReply(rd *proto.Reader) (err error) {
data, err := rd.ReadReply()
if err != nil {
return err
}
resultMap := map[string]interface{}{}
switch midResponse := data.(type) {
case map[interface{}]interface{}: // resp3 will return map
for k, v := range midResponse {
stringKey, ok := k.(string)
if !ok {
return fmt.Errorf("redis: invalid map key %#v", k)
}
resultMap[stringKey] = v
}
case []interface{}: // resp2 will return array of arrays
n := len(midResponse)
for i := 0; i < n; i++ {
finalArr, ok := midResponse[i].([]interface{}) // final array that we need to transform to map
if !ok {
return fmt.Errorf("redis: unexpected response %#v", data)
}
m := len(finalArr)
if m%2 != 0 { // since this should be map, keys should be even number
return fmt.Errorf("redis: unexpected response %#v", data)
}
for j := 0; j < m; j += 2 {
stringKey, ok := finalArr[j].(string) // the first one
if !ok {View on GitHub (pinned to 36d97525cd)
Solutions
- If you do not need RESP3, connect with RESP2 (do not set Options.Protocol=3) so the parser takes the array-of-arrays branch.
- Upgrade the RediSearch module and Redis to a version whose FT.CONFIG GET reply uses string keys.
- Confirm with redis-cli (`HELLO 3` then `FT.CONFIG GET <option>`) what key types are returned.
- Avoid FT.CONFIG GET for unknown/unsupported options that yield unusual replies.
Example fix
// before (RESP3)
client := redis.NewClient(&redis.Options{Addr: ":6379", Protocol: 3})
v, err := client.FTConfigGet(ctx, "EXTLOAD").Result()
// after (fall back to RESP2 if the module emits non-string keys)
client := redis.NewClient(&redis.Options{Addr: ":6379"}) // Protocol defaults to RESP2 Defensive patterns
Strategy: validation
Validate before calling
// If RESP3 triggers non-string keys, prefer RESP2 for FT.CONFIG GET.
if client.Options().Protocol == 3 {
// open a separate RESP2 client for module config calls, or drop Protocol:3
} Try / catch
v, err := client.FTConfigGet(ctx, option).Result()
if err != nil {
// retry on a RESP2 connection if RESP3 module reply is malformed
v, err = resp2Client.FTConfigGet(ctx, option).Result()
return v, err
} Prevention
- Match RESP protocol version to module support.
- Keep RediSearch module and go-redis versions compatible.
When it happens
Trigger: client.FTConfigGet(ctx, option) over a RESP3 connection (Options.Protocol == 3) when the RediSearch/module reply contains a non-string map key (e.g. an integer or bulk-encoded integer key), or when the reply shape is not what the parser assumes.
Common situations: Enabling RESP3 against a RediSearch/module version whose CONFIG GET reply uses non-string keys; a module bug or version mismatch; a proxy that re-types keys.
Related errors
- redis: unexpected response %#v
- invalid results format: expected map, got %T
- redis: RESET is not allowed when client-side caching is enab
- invalid client type
- invalid notification format
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/641ff87388984b7b.json.
Report an issue: GitHub.