redis/go-redis · error
failed to create connection handoff metric: %w
Error message
failed to create connection handoff metric: %w
What it means
Returned by createRecorder when meter.Int64Counter fails to create the redis.client.connection.handoff counter, which counts connections handed off to another node after MOVING maintenance notifications. Init aborts with 'failed to create metrics recorder' and observability stays disabled. The failure originates in the MeterProvider's instrument registration.
Source
Thrown at extra/redisotel-native/redisotel.go:241
return nil, fmt.Errorf("failed to create connection create time histogram: %w", err)
}
connectionCreateTime = connectionCreateTimeConv.Inst()
connectionRelaxedTimeout, err = meter.Int64UpDownCounter(
MetricConnectionRelaxedTimeout,
metric.WithDescription("How many times the connection timeout has been increased/decreased (after a server maintenance notification)"),
metric.WithUnit("{relaxation}"),
)
if err != nil {
return nil, fmt.Errorf("failed to create connection relaxed timeout metric: %w", err)
}
connectionHandoff, err = meter.Int64Counter(
MetricConnectionHandoff,
metric.WithDescription("Connections that have been handed off to another node (e.g after a MOVING notification)"),
)
if err != nil {
return nil, fmt.Errorf("failed to create connection handoff metric: %w", err)
}
}
var clientErrors metric.Int64Counter
var maintenanceNotifications metric.Int64Counter
if cfg.isMetricGroupEnabled(MetricGroupResiliency) {
clientErrors, err = meter.Int64Counter(
MetricClientErrors,
metric.WithDescription("Number of errors handled by the Redis client"),
metric.WithUnit("{error}"),
)
if err != nil {
return nil, fmt.Errorf("failed to create client errors metric: %w", err)
}
maintenanceNotifications, err = meter.Int64Counter(
MetricMaintenanceNotifications,View on GitHub (pinned to c5cad058c7)
Solutions
- Initialize otel MeterProvider before redisotel Init and do not shut it down until after redisotel Shutdown
- Search the codebase for duplicate registrations of redis.client.connection.handoff and reconcile name/unit/description
- Inspect the wrapped error and otel's global error handler output for the provider's reason
- Pin go.opentelemetry.io/otel versions consistently across all modules (go mod tidy)
- As a stopgap, clear the MetricGroupFlagConnectionBasic bit in Config.MetricGroups
Example fix
// before
defer provider.Shutdown()
obs.Init(cfg) // runs after shutdown in some code path
// after
if err := obs.Init(cfg); err != nil { log.Fatal(err) }
runtime.AddCleanup or explicit ordered teardown: obs.Shutdown() then provider.Shutdown() Defensive patterns
Strategy: validation
Validate before calling
if cfg.Enabled && cfg.MeterProvider == nil {
cfg.MeterProvider = globalSDKMeterProvider // set during app bootstrap
}
if globalSDKMeterProvider == nil { return errors.New("no metric SDK configured") } Try / catch
err := obs.Init(cfg)
if err != nil {
log.Printf("observability disabled (handoff metric): %v", err)
} Prevention
- Bootstrap order: meter provider -> redisotel Init -> redis clients
- Do not register redis.client.connection.handoff elsewhere
- Check and log Init errors
- Keep otel versions uniform
When it happens
Trigger: Init with MetricGroupFlagConnectionBasic enabled while the configured (or global) MeterProvider has been Shutdown, is a stub, or rejects the instrument (e.g. duplicate name registered with different options in providers that validate this).
Common situations: Startup ordering bugs where telemetry is shut down before Redis clients init; two instrumentation packages registering the same metric name; minimal test MeterProviders that error on any registration.
Related errors
- failed to create operation duration histogram: %w
- failed to create connection count metric: %w
- failed to create metrics recorder: %w
- failed to create connection create time histogram: %w
- failed to create connection relaxed timeout metric: %w
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/78c876947f165a37.
Report an issue: GitHub.