{"id":"3c7a62ff21587571","repo":"go-redis/redis","slug":"redis-can-t-parse-map-string-slice-interface-repl","errorCode":null,"errorMessage":"redis: can't parse map-string-slice-interface reply: unexpected type %c","messagePattern":"redis: can't parse map-string-slice-interface reply: unexpected type %c","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"command.go","lineNumber":2634,"sourceCode":"\t\t\tkey, err := rd.ReadString()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcmd.val[key] = make([]interface{}, 0, itemLen-1)\n\t\t\tfor j := 1; j < itemLen; j++ {\n\t\t\t\t// Read the inner array for timestamp-value pairs\n\t\t\t\tdata, err := rd.ReadReply()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tcmd.val[key] = append(cmd.val[key], data)\n\t\t\t}\n\t\t}\n\tdefault:\n\t\t// Any other reply type leaves the peeked frame unread. Returning nil\n\t\t// here would put the connection back in the pool with those bytes\n\t\t// buffered, so the next command reads them as its own reply.\n\t\treturn fmt.Errorf(\"redis: can't parse map-string-slice-interface reply: unexpected type %c\", readType)\n\t}\n\n\treturn nil\n}\n\nfunc (cmd *MapStringSliceInterfaceCmd) Clone() Cmder {\n\tvar val map[string][]interface{}\n\tif cmd.val != nil {\n\t\tval = make(map[string][]interface{}, len(cmd.val))\n\t\tfor k, v := range cmd.val {\n\t\t\tif v != nil {\n\t\t\t\tnewSlice := make([]interface{}, len(v))\n\t\t\t\tcopy(newSlice, v)\n\t\t\t\tval[k] = newSlice\n\t\t\t}\n\t\t}\n\t}\n\treturn &MapStringSliceInterfaceCmd{","sourceCodeStart":2616,"sourceCodeEnd":2652,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/command.go#L2616-L2652","documentation":"Returned by MapStringSliceInterfaceCmd.readReply (command.go:2634, used by TimeSeries module commands like TS.RANGE/TS.MRANGE/TS.INFO variants) when the peeked RESP type is neither a map (RespMap, RESP3) nor an array (RespArray, RESP2). Returning nil would leave the unread frame buffered and corrupt the next reply on the pooled connection, so the parser returns this error to force the connection to be dropped.","triggerScenarios":"Calling a TimeSeries command and the server returns a non-map/non-array reply — e.g. a WRONGTYPE error framed as a simple-string/error, or a status reply, or a bulk scalar; or a server/proxy that downgrades RESP3 negotiation silently.","commonSituations":"RedisTimeSeries not loaded (command returns an error simple-string); key of wrong type (WRONGTYPE); a proxy (twemproxy, Envoy) stripping RESP3 capability; version mismatch where the module changed its reply shape.","solutions":["Verify the RedisTimeSeries module is loaded (MODULE LIST) before calling TS.* commands.","Ensure the key is of the correct type (TS.TYPE) — use EXISTS/TYPE first.","Confirm RESP3 is consistently negotiated end-to-end (no proxy downgrade) or use RESP2 consistently.","On this error the connection is poisoned; let the pool reclaim it (do not reuse the specific conn)."],"exampleFix":"// before — module not loaded / wrong type\nres, err := rdb.TSRange(ctx, &redis.TSRangeOptions{Key: \"k\"}).Result()\n// → error: unexpected type +\n\n// after — verify module + key type first\nif t, err := rdb.Type(ctx, \"k\").Result(); err != nil || t != \"TSDB-TYPE\" {\n    return fmt.Errorf(\"key is not a timeseries (got %s, err=%v)\", t, err)\n}","handlingStrategy":"try-catch","validationCode":"// Verify the TimeSeries module is loaded and the key type is correct before calling TS.*.\nif t, err := rdb.Type(ctx, key).Result(); err != nil || t != \"TSDB-TYPE\" {\n    return fmt.Errorf(\"not a timeseries: type=%s err=%v\", t, err)\n}","typeGuard":null,"tryCatchPattern":"res, err := rdb.TSRange(ctx, opts).Result()\nif err != nil && strings.Contains(err.Error(), \"can't parse map-string-slice-interface\") {\n    // non-map/non-array reply — check module loaded, key type, RESP3 negotiation\n}","preventionTips":["Check MODULE LIST for RedisTimeSeries before calling TS.* commands.","Verify key type with TYPE before reading.","Ensure RESP3 is negotiated end-to-end (no proxy downgrade).","Treat the connection as poisoned after this error; let the pool reclaim it."],"tags":["command","reply-parsing","timeseries","resp3","module","protocol-mismatch"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}