go-redis/redis · critical

redis: no sentinels configured

Error message

redis: no sentinels configured

What it means

Returned by the sentinel failover client when there are no sentinel addresses configured (c.sentinelAddrs is empty) after the initial sentinel lookup path fails to find a master. The failover client needs at least one Sentinel to discover the current master; with none configured it cannot proceed.

Source

Thrown at sentinel.go:1000

	if c.sentinel != nil {
		addr, err := c.getMasterAddr(ctx, c.sentinel)
		if err != nil {
			_ = c.closeSentinel()
			if isContextError(ctx.Err()) {
				return "", err
			}
			// Continue on other errors
			internal.Logger.Printf(ctx, "sentinel: GetMasterAddrByName name=%q failed: %s",
				c.opt.MasterName, err)
		} else {
			return addr, nil
		}
	}

	// short circuit if no sentinels configured
	if len(c.sentinelAddrs) == 0 {
		return "", errors.New("redis: no sentinels configured")
	}

	var (
		masterAddr string
		wg         sync.WaitGroup
		once       sync.Once
		errCh      = make(chan error, len(c.sentinelAddrs))
	)

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	for i, sentinelAddr := range c.sentinelAddrs {
		wg.Add(1)
		go func(i int, addr string) {
			defer wg.Done()
			sentinelCli := NewSentinelClient(c.opt.sentinelOptions(addr))
			addrVal, err := sentinelCli.GetMasterAddrByName(ctx, c.opt.MasterName).Result()

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Provide at least one sentinel address in FailoverOptions.SentinelAddrs (one per Sentinel instance).
  2. Validate that the config source (env var, file) actually populated SentinelAddrs before constructing the client.
  3. Add a startup assertion: if len(opts.SentinelAddrs) == 0 { return error }.

Example fix

// before — MasterName set but no sentinels
client := redis.NewFailoverClient(&redis.FailoverOptions{
    MasterName: "mymaster",
    // SentinelAddrs missing/empty
})
// client.Get(ctx, "k").Err() => redis: no sentinels configured

// after
client := redis.NewFailoverClient(&redis.FailoverOptions{
    MasterName:   "mymaster",
    SentinelAddrs: []string{"sentinel-1:26379", "sentinel-2:26379"},
})
Defensive patterns

Strategy: validation

Validate before calling

func validateFailoverOpts(o *redis.FailoverOptions) error {
    if o.MasterName == "" {
        return errors.New("MasterName is required")
    }
    if len(o.SentinelAddrs) == 0 {
        return errors.New("SentinelAddrs is empty; provide at least one sentinel host:port")
    }
    return nil
}

Prevention

When it happens

Trigger: Creating a FailoverClient with an empty SentinelAddrs slice and no MasterName-driven discovery. SentinelAddrs was set only on Options but the failover options path didn't receive them. A misconfigured SentinelOptions where the addrs slice is nil/empty.

Common situations: Configuring redis.NewFailoverClient with MasterName but forgetting SentinelAddrs. Environment/config loading that returns an empty address list (env var not set, config file parse error). Copy-paste errors omitting the SentinelAddrs field.

Related errors


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