{"record":{"id":"d482d54b9a38099e","repo":"redis/go-redis","slug":"failed-to-create-operation-duration-histogram-w","errorCode":null,"errorMessage":"failed to create operation duration histogram: %w","messagePattern":"failed to create operation duration histogram: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extra/redisotel-native/redisotel.go","lineNumber":194,"sourceCode":"\t}\n}\n\n// createRecorder creates a metricsRecorder with all instruments based on config.\nfunc (o *ObservabilityInstance) createRecorder(meter metric.Meter, cfg config) (*metricsRecorder, error) {\n\tvar err error\n\n\tvar operationDuration metric.Float64Histogram\n\tif cfg.isMetricGroupEnabled(MetricGroupCommand) {\n\t\tvar operationDurationOpts []metric.Float64HistogramOption\n\t\tif cfg.histAggregation == HistogramAggregationExplicitBucket {\n\t\t\toperationDurationOpts = append(operationDurationOpts,\n\t\t\t\tmetric.WithExplicitBucketBoundaries(cfg.bucketsOperationDuration...),\n\t\t\t)\n\t\t}\n\t\tvar operationDurationConv dbconv.ClientOperationDuration\n\t\toperationDurationConv, err = dbconv.NewClientOperationDuration(meter, operationDurationOpts...)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create operation duration histogram: %w\", err)\n\t\t}\n\t\toperationDuration = operationDurationConv.Inst()\n\t}\n\n\tvar connectionCount metric.Int64UpDownCounter // OTel semconv: UpDownCounter\n\tvar connectionCreateTime metric.Float64Histogram\n\tvar connectionRelaxedTimeout metric.Int64UpDownCounter\n\tvar connectionHandoff metric.Int64Counter\n\n\tif cfg.isMetricGroupEnabled(MetricGroupConnectionBasic) {\n\t\tconnectionCount, err = meter.Int64UpDownCounter(\n\t\t\tdbconv.ClientConnectionCount{}.Name(),\n\t\t\tmetric.WithDescription(dbconv.ClientConnectionCount{}.Description()),\n\t\t\tmetric.WithUnit(dbconv.ClientConnectionCount{}.Unit()),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create connection count metric: %w\", err)\n\t\t}","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/extra/redisotel-native/redisotel.go#L176-L212","documentation":"This error is returned from createRecorder in the redisotel-native package when the OpenTelemetry meter fails to create the db.client.operation.duration histogram instrument (via dbconv.NewClientOperationDuration). Init wraps it as 'failed to create metrics recorder' and aborts initialization, so no Redis client metrics are collected. Instrument creation essentially only fails when the MeterProvider is misconfigured, shut down, or a global error handler reports an invalid instrument (bad name/unit/boundaries).","triggerScenarios":"Calling ObservabilityInstance.Init with a Config that enables MetricGroupFlagCommand while the MeterProvider is nil-and-global-otel is a no-op provider, the provider was already Shutdown(), a custom MeterProvider returns errors for instrument creation, or HistogramAggregationExplicitBucket is set with malformed BucketsOperationDuration.","commonSituations":"Init is called after otel metric provider shutdown in tests; app forgets to install an SDK MeterProvider so the global no-op provider is used; swapping to a Prometheus or custom reader that rejects the instrument config; upgrading go.opentelemetry.io/otel and hitting semconv/dbconv incompatibilities.","solutions":["Install and set a real metric SDK MeterProvider (e.g. sdkmetric.NewMeterProvider with a Reader) in cfg.MeterProvider, or via otel.SetMeterProvider, before calling Init","Ensure the MeterProvider has not been Shutdown before Init; re-create the provider if it was","Check the wrapped error (%w) with errors.Unwrap / otel's global error handler logs to identify the specific instrument-creation failure","Validate BucketsOperationDuration values (positive, increasing) if using HistogramAggregationExplicitBucket","Capture the error from Init at startup instead of ignoring it, and fail fast or fall back to disabled metrics"],"exampleFix":"// before\nerr := obs.Init(&redisotel.Config{Enabled: true}) // uses no-op global provider\n// after\nprovider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(sdkmetric.NewPeriodicReader(exp exporter)))\nerr := obs.Init(&redisotel.Config{Enabled: true, MeterProvider: provider})","handlingStrategy":"validation","validationCode":"if cfg.Enabled && cfg.MeterProvider == nil && otel.GetMeterProvider() == noop.NewMeterProvider() {\n    // no SDK installed; Init will fail or no-op\n}\n// prefer: pass an explicit sdkmetric.MeterProvider created and not yet Shutdown","typeGuard":"func hasLiveMeterProvider(p metric.MeterProvider) bool { return p != nil }","tryCatchPattern":"if err := obs.Init(cfg); err != nil {\n    var root error = errors.Unwrap(err)\n    otel.Handle(err)\n    log.Printf(\"redisotel disabled: %v (root: %v)\", err, root)\n}","preventionTips":["Always install an sdkmetric MeterProvider before Init","Never Shutdown the meter provider before redisotel Init","Always check the error returned by Init at startup","Pin consistent go.opentelemetry.io/otel versions across modules"],"tags":["opentelemetry","metrics","initialization","go"],"backgroundTag":"otel-instrument-creation-failed","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}