go-redis/redis · error

redis: can't parse map-string-slice-interface reply: unexpec

Error message

redis: can't parse map-string-slice-interface reply: unexpected type %c

What it means

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.

Source

Thrown at command.go:2634

			key, err := rd.ReadString()
			if err != nil {
				return err
			}
			cmd.val[key] = make([]interface{}, 0, itemLen-1)
			for j := 1; j < itemLen; j++ {
				// Read the inner array for timestamp-value pairs
				data, err := rd.ReadReply()
				if err != nil {
					return err
				}
				cmd.val[key] = append(cmd.val[key], data)
			}
		}
	default:
		// Any other reply type leaves the peeked frame unread. Returning nil
		// here would put the connection back in the pool with those bytes
		// buffered, so the next command reads them as its own reply.
		return fmt.Errorf("redis: can't parse map-string-slice-interface reply: unexpected type %c", readType)
	}

	return nil
}

func (cmd *MapStringSliceInterfaceCmd) Clone() Cmder {
	var val map[string][]interface{}
	if cmd.val != nil {
		val = make(map[string][]interface{}, len(cmd.val))
		for k, v := range cmd.val {
			if v != nil {
				newSlice := make([]interface{}, len(v))
				copy(newSlice, v)
				val[k] = newSlice
			}
		}
	}
	return &MapStringSliceInterfaceCmd{

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Verify the RedisTimeSeries module is loaded (MODULE LIST) before calling TS.* commands.
  2. Ensure the key is of the correct type (TS.TYPE) — use EXISTS/TYPE first.
  3. Confirm RESP3 is consistently negotiated end-to-end (no proxy downgrade) or use RESP2 consistently.
  4. On this error the connection is poisoned; let the pool reclaim it (do not reuse the specific conn).

Example fix

// before — module not loaded / wrong type
res, err := rdb.TSRange(ctx, &redis.TSRangeOptions{Key: "k"}).Result()
// → error: unexpected type +

// after — verify module + key type first
if t, err := rdb.Type(ctx, "k").Result(); err != nil || t != "TSDB-TYPE" {
    return fmt.Errorf("key is not a timeseries (got %s, err=%v)", t, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the TimeSeries module is loaded and the key type is correct before calling TS.*.
if t, err := rdb.Type(ctx, key).Result(); err != nil || t != "TSDB-TYPE" {
    return fmt.Errorf("not a timeseries: type=%s err=%v", t, err)
}

Try / catch

res, err := rdb.TSRange(ctx, opts).Result()
if err != nil && strings.Contains(err.Error(), "can't parse map-string-slice-interface") {
    // non-map/non-array reply — check module loaded, key type, RESP3 negotiation
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/3c7a62ff21587571.json. Report an issue: GitHub.