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

  1. Find the duplicate: grep for `view.Register` and any opencensus.ServerOption/opencensus.DialOption in your code.
  2. Remove the conflicting registration so observability.Start owns these views, or skip cloud_monitoring in the config.
  3. Call observability.Start exactly once per process; in tests call observability.End() and view.Unregister(defaultViews...) between cases.
  4. 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

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


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)