grpc/grpc-go · error
failed to register observability views
Error message
failed to register observability views: %v
What it means
view.Register(defaultViews...) returned an error, which for OpenCensus means one of the default gRPC views is already registered. The default views are the opencensus client/server RPC views; the OpenCensus spec returns an error on duplicate name registration. Emitted at opencensus.go:146.
Solutions
- Find the duplicate: grep for `view.Register` and any opencensus.ServerOption/opencensus.DialOption in your code.
- Remove the conflicting registration so observability.Start owns these views, or skip cloud_monitoring in the config.
- Call observability.Start exactly once per process; in tests call observability.End() and view.Unregister(defaultViews...) between cases.
- If you need both instrumentations, register only non-overlapping views in your code.
Example fix
// before view.Register(opencensus.ClientStartedRPCsView) // app code observability.Start(ctx) // plugin re-registers -> error // after // remove the app-side registration; let the plugin own the grpc views observability.Start(ctx)
Defensive patterns
Strategy: validation
Validate before calling
// Detect duplicate registration before Start.
func viewsAlreadyRegistered() error {
for _, v := range defaultViews {
if got, err := view.Find(v.Name); err == nil && got != nil {
return fmt.Errorf("view %s already registered; unregister it or skip cloud_monitoring", v.Name)
}
}
return nil
}
if err := viewsAlreadyRegistered(); err != nil { return err } Try / catch
if err := observability.Start(ctx); err != nil {
if strings.Contains(err.Error(), "failed to register observability views") {
// duplicate registration — unregister the conflicting views and skip, or drop cloud_monitoring
view.Unregister(defaultViews...)
return err
}
return err
} Prevention
- Let the observability plugin own the grpc opencensus default views; don't register them elsewhere.
- Call Start exactly once per process.
- In tests, view.Unregister(defaultViews...) and observability.End() between cases.
When it happens
Trigger: Another part of the app (or another call path) already registered one of opencensus.ClientStartedRPCsView, ClientCompletedRPCsView, ServerStartedRPCsView, etc. Common when the app also uses go.opencensus.io/stats/opencensus directly, or when observability.Start is called twice.
Common situations: Mixing the grpc-gcp-observability plugin with direct opencensus view registration; calling Start more than once (e.g. in tests without resetting state); an unrelated library that registers the grpc default views; partial Start failure leaving views registered, then retry.
Related errors
- failed to create Stackdriver exporter
- failed to instrument OpenCensus
- cannot have a leading slash
- cannot have exclude and a '*' wildcard
- empty destination project ID
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/d48f4e710a54632b.
Report an issue: GitHub.
Appendix: source
Thrown at gcp/observability/opencensus.go:146
return nil
}
var err error
exporter, err = newExporter(config)
if err != nil {
return err
}
var to opencensus.TraceOptions
if config.CloudTrace != nil {
to.TS = trace.ProbabilitySampler(config.CloudTrace.SamplingRate)
trace.RegisterExporter(exporter.(trace.Exporter))
logger.Infof("Start collecting and exporting trace spans with global_trace_sampling_rate=%.2f", config.CloudTrace.SamplingRate)
}
if config.CloudMonitoring != nil {
if err := view.Register(defaultViews...); err != nil {
return fmt.Errorf("failed to register observability views: %v", err)
}
view.SetReportingPeriod(defaultMetricsReportingInterval)
view.RegisterExporter(exporter.(view.Exporter))
logger.Infof("Start collecting and exporting metrics")
}
internal.AddGlobalServerOptions.(func(opt ...grpc.ServerOption))(opencensus.ServerOption(to))
internal.AddGlobalDialOptions.(func(opt ...grpc.DialOption))(opencensus.DialOption(to))
logger.Infof("Enabled OpenCensus StatsHandlers for clients and servers")
return nil
}
// stopOpenCensus flushes the exporter's and cleans up globals across all
// packages if exporter was created.
func stopOpenCensus() {
if exporter != nil {
internal.ClearGlobalDialOptions()View on GitHub (pinned to 0c51461d27)