go-redis/redis · error

redis: got %d elements in the sorted set array, wanted a mul

Error message

redis: got %d elements in the sorted set array, wanted a multiple of 2

What it means

Returned by ZSliceCmd.readReply (command.go:4423, used by ZRangeWithScores, ZPopMin/Max, ZInterWithScores, ZUnionWithScores, ZRangeByScoreWithScores, ZRandMemberWithScores, ZDiffWithScores). The reply is either a RESP3 array-of-pairs or a RESP2 flat [member,score,member,score,...] array; a flat RESP2 array with an odd element count cannot be split into member/score pairs and is reported rather than silently mis-parsing.

Source

Thrown at command.go:4423

	}

	// If the n is 0, can't continue reading.
	if n == 0 {
		cmd.val = make([]Z, 0)
		return nil
	}

	typ, err := rd.PeekReplyType()
	if err != nil {
		return err
	}
	array := typ == proto.RespArray

	if array {
		cmd.val = make([]Z, n)
	} else {
		if n%2 != 0 {
			return fmt.Errorf("redis: got %d elements in the sorted set array, wanted a multiple of 2", n)
		}
		cmd.val = make([]Z, n/2)
	}

	for i := 0; i < len(cmd.val); i++ {
		if array {
			if err = rd.ReadFixedArrayLen(2); err != nil {
				return err
			}
		}

		if cmd.val[i].Member, err = rd.ReadString(); err != nil {
			return err
		}

		if cmd.val[i].Score, err = rd.ReadFloat(); err != nil {
			return err
		}

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Switch the client to RESP3 (Options.Protocol = 3) so the reply arrives as a proper array-of-pairs.
  2. Remove or bypass any proxy that may be rewriting RESP frames, and connect directly to Redis.
  3. Capture a RESP trace (tcpdump/redis-cli) to confirm whether Redis or an intermediary is producing the odd length; file a bug against the offender.

Example fix

// before
opts := redis.Options{Addr: addr /* RESP2 */}
rdb := redis.NewClient(&opts)
members, err := rdb.ZRangeWithScores(ctx, "s", 0, -1).Result() // odd length → error

// after
opts := redis.Options{Addr: addr, Protocol: 3}
rdb := redis.NewClient(&opts)
members, err := rdb.ZRangeWithScores(ctx, "s", 0, -1).Result()
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer RESP3 so sorted-set *WithScores commands arrive as array-of-pairs.
opts := redis.Options{Addr: addr, Protocol: 3}

Try / catch

members, err := rdb.ZRangeWithScores(ctx, "s", 0, -1).Result()
if err != nil && strings.Contains(err.Error(), "sorted set array, wanted a multiple of 2") {
    // odd-length RESP2 array — switch to RESP3 or bypass any proxy
}

Prevention

When it happens

Trigger: Calling a *WithScores sorted-set command with the client on RESP2 and the server returning an odd-length array — typically a sign of corruption, a proxy rewriting the frame, or a Redis build with a non-standard reply shape.

Common situations: A RESP-mangling proxy (twemproxy and friends) between client and Redis; an experimental/patched Redis; rare memory/link corruption. Legitimate Redis builds always return even-length flat arrays for these commands.

Related errors


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