{"record":{"id":"d26876eb1d7bdf12","repo":"redis/go-redis","slug":"redisotel-t-not-supported","errorCode":null,"errorMessage":"redisotel: %T not supported","messagePattern":"redisotel: %T not supported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extra/redisotel/metrics.go","lineNumber":55,"sourceCode":"\t\t\tmetric.WithInstrumentationVersion(\"semver:\"+redis.Version()),\n\t\t)\n\t}\n\tif conf.poolName == \"\" {\n\t\tswitch rdb := rdb.(type) {\n\t\tcase *redis.Client:\n\t\t\tconf.poolName = rdb.Options().Addr\n\t\tcase *redis.ClusterClient:\n\t\t\tfor _, addr := range rdb.Options().Addrs {\n\t\t\t\tconf.poolName = addr\n\t\t\t\tbreak\n\t\t\t}\n\t\tcase *redis.Ring:\n\t\t\tfor _, addr := range rdb.Options().Addrs {\n\t\t\t\tconf.poolName = addr\n\t\t\t\tbreak\n\t\t\t}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"redisotel: %T not supported\", rdb)\n\t\t}\n\t}\n\tconf.attrs = append(conf.attrs, attribute.String(\"pool.name\", conf.poolName))\n\n\tvar state *metricsState\n\tif conf.closeChan != nil {\n\t\tstate = &metricsState{\n\t\t\tregistrations: make([]metric.Registration, 0),\n\t\t\tclosed:        false,\n\t\t\tmutex:         sync.Mutex{},\n\t\t}\n\n\t\tgo func() {\n\t\t\t<-conf.closeChan\n\n\t\t\tstate.mutex.Lock()\n\t\t\tstate.closed = true\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/extra/redisotel/metrics.go#L37-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"Using redis.NewFailoverClient (sentinel) with redisotel metrics; wrapping clients in a custom struct that implements UniversalClient; tests injecting mocks.","solutions":["Use a supported client type: Client, ClusterClient, or Ring","For sentinel failover, instrument via a wrapper or check for newer redisotel versions that add FailoverClient support","Ensure the variable's concrete type is one of the supported types (e.g. not a generic interface parameter)","Instrument each underlying *redis.Client directly if you manage failover manually"],"exampleFix":"// before\nrdb := redis.NewFailoverClient(failoverOpt)\nredisotel.InstrumentMetrics(rdb) // panics into: redisotel: *redis.FailoverClient not supported\n// after\nrdb := redis.NewClient(&redis.Options{Addr: \"localhost:6379\"})\nredisotel.InstrumentMetrics(rdb)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func instrumentable(rdb redis.UniversalClient) bool {\n\tswitch rdb.(type) {\n\tcase *redis.Client, *redis.ClusterClient, *redis.Ring:\n\t\treturn rdb != nil\n\tdefault:\n\t\treturn false\n\t}\n}","tryCatchPattern":"if err := redisotel.InstrumentMetrics(rdb); err != nil {\n\tif strings.Contains(err.Error(), \"not supported\") {\n\t\tlog.Printf(\"metrics unsupported for %T\", rdb)\n\t}\n}","preventionTips":["Only call redisotel on Client/ClusterClient/Ring","For sentinel, instrument underlying per-node clients or upgrade redisotel","Avoid storing clients in a lossy UniversalClient variable when instrumenting"],"tags":["opentelemetry","metrics","type-mismatch","unsupported-client"],"backgroundTag":"unsupported-client-type","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}