redis/go-redis · error

redisotel: %T not supported

Error message

redisotel: %T not supported

What it means

redisotel.InstrumentMetrics only supports *redis.Client, *redis.ClusterClient, and *redis.Ring. When a UniversalClient holds a concrete type outside this set (e.g. a custom implementation, a failover client reached through an interface wrapper, or a nil client), the default case returns '%T not supported'. Note the failover client (*redis.FailoverClient via redis.NewFailoverClient) is NOT in the switch, so it hits this error.

Source

Thrown at extra/redisotel/metrics.go:55

			metric.WithInstrumentationVersion("semver:"+redis.Version()),
		)
	}
	if conf.poolName == "" {
		switch rdb := rdb.(type) {
		case *redis.Client:
			conf.poolName = rdb.Options().Addr
		case *redis.ClusterClient:
			for _, addr := range rdb.Options().Addrs {
				conf.poolName = addr
				break
			}
		case *redis.Ring:
			for _, addr := range rdb.Options().Addrs {
				conf.poolName = addr
				break
			}
		default:
			return fmt.Errorf("redisotel: %T not supported", rdb)
		}
	}
	conf.attrs = append(conf.attrs, attribute.String("pool.name", conf.poolName))

	var state *metricsState
	if conf.closeChan != nil {
		state = &metricsState{
			registrations: make([]metric.Registration, 0),
			closed:        false,
			mutex:         sync.Mutex{},
		}

		go func() {
			<-conf.closeChan

			state.mutex.Lock()
			state.closed = true

View on GitHub (pinned to c5cad058c7)

Solutions

  1. Use a supported client type: Client, ClusterClient, or Ring
  2. For sentinel failover, instrument via a wrapper or check for newer redisotel versions that add FailoverClient support
  3. Ensure the variable's concrete type is one of the supported types (e.g. not a generic interface parameter)
  4. Instrument each underlying *redis.Client directly if you manage failover manually

Example fix

// before
rdb := redis.NewFailoverClient(failoverOpt)
redisotel.InstrumentMetrics(rdb) // panics into: redisotel: *redis.FailoverClient not supported
// after
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
redisotel.InstrumentMetrics(rdb)
Defensive patterns

Strategy: type-guard

Type guard

func instrumentable(rdb redis.UniversalClient) bool {
	switch rdb.(type) {
	case *redis.Client, *redis.ClusterClient, *redis.Ring:
		return rdb != nil
	default:
		return false
	}
}

Try / catch

if err := redisotel.InstrumentMetrics(rdb); err != nil {
	if strings.Contains(err.Error(), "not supported") {
		log.Printf("metrics unsupported for %T", rdb)
	}
}

Prevention

When it happens

Trigger: Calling InstrumentMetrics with a client whose dynamic type is not *redis.Client, *redis.ClusterClient, or *redis.Ring — most commonly a Sentinel failover client or a stub/mock UniversalClient, or passing a nil client.

Common situations: Using redis.NewFailoverClient (sentinel) with redisotel metrics; wrapping clients in a custom struct that implements UniversalClient; tests injecting mocks.

Related errors


AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01). Data as JSON: /api/errors/d26876eb1d7bdf12. Report an issue: GitHub.