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

  1. Initialize otel MeterProvider before redisotel Init and do not shut it down until after redisotel Shutdown
  2. Search the codebase for duplicate registrations of redis.client.connection.handoff and reconcile name/unit/description
  3. Inspect the wrapped error and otel's global error handler output for the provider's reason
  4. Pin go.opentelemetry.io/otel versions consistently across all modules (go mod tidy)
  5. 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

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


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