go-redis/redis · error

redis: Watch requires at least one key

Error message

redis: Watch requires at least one key

What it means

Returned by Ring.Watch when the keys slice is empty. A Ring transaction must lock keys on a single shard, so at least one key is required; calling Watch with no keys is a programmer error and is rejected before any shard lookup.

Source

Thrown at ring.go:965

			if err = hook(ctx, cmds); err != nil {
				errs <- err
			}
		}(hash, cmds)
	}

	wg.Wait()
	close(errs)

	if err := <-errs; err != nil {
		return err
	}
	return cmdsFirstErr(cmds)
}

func (c *Ring) Watch(ctx context.Context, fn func(*Tx) error, keys ...string) error {
	if len(keys) == 0 {
		return fmt.Errorf("redis: Watch requires at least one key")
	}

	var shards []*ringShard

	for _, key := range keys {
		if key != "" {
			shard, err := c.sharding.GetByKey(key)
			if err != nil {
				return err
			}

			shards = append(shards, shard)
		}
	}

	if len(shards) == 0 {
		return fmt.Errorf("redis: Watch requires at least one shard")
	}

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Guard the Watch call site: only invoke Watch when len(keys) > 0.
  2. If an empty key set is legitimately possible in your flow, handle it without a transaction (e.g. skip or use a non-transactional path).
  3. Audit call sites that build the keys slice to ensure they never produce an empty result.

Example fix

// before
err := ring.Watch(ctx, fn) // no keys -> error

// after
if len(keys) == 0 {
    return errors.New("cannot run transaction without keys")
}
err := ring.Watch(ctx, fn, keys...)
Defensive patterns

Strategy: validation

Validate before calling

if len(keys) == 0 {
    return errors.New("Ring.Watch requires at least one key")
}
return ring.Watch(ctx, fn, keys...)

Try / catch

if err := ring.Watch(ctx, fn, keys...); err != nil {
    if strings.Contains(err.Error(), "requires at least one key") {
        // programmer error: fix the call site
    }
}

Prevention

When it happens

Trigger: Calling ring.Watch(ctx, fn) with no key arguments, or passing a keys slice that resolves to length zero (e.g. unpacking an empty slice via keys...).

Common situations: Dynamic key list computed at runtime that happened to be empty; refactoring that dropped the key argument; calling Watch with a pre-sliced variadic that was filtered down to nothing.

Related errors


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