thanos-io/thanos · error

MSet the length of keys and values not equal, len(keys)=

Error message

MSet the length of keys and values not equal, len(keys)=%d, len(values)=%d

What it means

RedisClient.MSet pipelines one SET command per key and requires the keys and values slices to be parallel arrays of equal length. If the lengths differ it returns immediately with this error rather than silently storing a partial batch.

Solutions

  1. Fix the caller to construct keys and values together so they stay index-aligned.
  2. Add an assertion in the batching loop that both slices grow in lockstep.
  3. If values may be missing, append nil placeholders to keep lengths equal before calling MSet.
  4. Read the len(keys)/len(values) numbers in the message to find which side is short and by how much.

Example fix

// before
values := results[:maxBatch] // can shorten values only
client.MSet(ctx, keys, values)
// after
if len(values) > len(keys) { values = values[:len(keys)] }
if len(keys) > len(values) { keys = keys[:len(values)] }
client.MSet(ctx, keys, values)
Defensive patterns

Strategy: validation

Validate before calling

if len(keys) != len(values) {
    return fmt.Errorf("MSet pre-check: len(keys)=%d len(values)=%d", len(keys), len(values))
}

Try / catch

if err := client.MSet(ctx, keys, values); err != nil {
    if strings.Contains(err.Error(), "length of keys and values not equal") {
        log.Errorf("batching bug: keys=%d values=%d", len(keys), len(values))
    }
    return err
}

Prevention

When it happens

Trigger: Calling MSet(ctx, keys, values) where len(keys) != len(values) — e.g. a caller builds keys from one list and values from a filtered/truncated list.

Common situations: Batching code that drops failed entries from values but not keys; off-by-one slicing; passing decoded results of unequal length from upstream parsing.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/a6df9e22fd248980. Report an issue: GitHub.

Appendix: source

Thrown at internal/cortex/chunk/cache/redis_client.go:114

	pingResp, err := resp.ToString()
	if err != nil {
		return errors.New("converting PING response to string")
	}
	if pingResp != "PONG" {
		return errors.Errorf("redis: Unexpected PING response %q", pingResp)
	}
	return nil
}

func (c *RedisClient) MSet(ctx context.Context, keys []string, values [][]byte) error {
	var cancel context.CancelFunc
	if c.timeout > 0 {
		ctx, cancel = context.WithTimeout(ctx, c.timeout)
		defer cancel()
	}

	if len(keys) != len(values) {
		return errors.Errorf("MSet the length of keys and values not equal, len(keys)=%d, len(values)=%d", len(keys), len(values))
	}

	cmds := make(rueidis.Commands, 0, len(keys))
	for i := range keys {
		cmds = append(cmds, c.rdb.B().Set().Key(keys[i]).Value(rueidis.BinaryString(values[i])).Ex(c.expiration).Build())
	}
	for _, resp := range c.rdb.DoMulti(ctx, cmds...) {
		if err := resp.Error(); err != nil {
			return err
		}
	}
	return nil
}

func (c *RedisClient) MGet(ctx context.Context, keys []string) ([][]byte, error) {
	var cancel context.CancelFunc
	if c.timeout > 0 {
		ctx, cancel = context.WithTimeout(ctx, c.timeout)

View on GitHub (pinned to 35b8b99117)