{"record":{"id":"6d3900e02797cc55","repo":"microsoft/aspire","slug":"runtime-metrics-w","errorCode":null,"errorMessage":"runtime metrics: %w","messagePattern":"runtime metrics: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Templating/Templates/go-starter/api/telemetry.go","lineNumber":79,"sourceCode":"\t)\n\totel.SetTracerProvider(tp)\n\n\tmetricOpts := []otlpmetricgrpc.Option{}\n\tif len(headers) > 0 {\n\t\tmetricOpts = append(metricOpts, otlpmetricgrpc.WithHeaders(headers))\n\t}\n\tmetricExp, err := otlpmetricgrpc.New(ctx, metricOpts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"metric exporter: %w\", err)\n\t}\n\tmp := sdkmetric.NewMeterProvider(\n\t\tsdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExp)),\n\t\tsdkmetric.WithResource(res),\n\t)\n\totel.SetMeterProvider(mp)\n\n\tif err := runtime.Start(runtime.WithMeterProvider(mp)); err != nil {\n\t\treturn nil, fmt.Errorf(\"runtime metrics: %w\", err)\n\t}\n\n\tlogOpts := []otlploggrpc.Option{}\n\tif len(headers) > 0 {\n\t\tlogOpts = append(logOpts, otlploggrpc.WithHeaders(headers))\n\t}\n\tlogExp, err := otlploggrpc.New(ctx, logOpts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"log exporter: %w\", err)\n\t}\n\tlp := sdklog.NewLoggerProvider(\n\t\tsdklog.WithProcessor(sdklog.NewBatchProcessor(logExp)),\n\t\tsdklog.WithResource(res),\n\t)\n\totellog.SetLoggerProvider(lp)\n\n\t// Route slog to OTel so structured logs appear in the dashboard.\n\tslog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Templating/Templates/go-starter/api/telemetry.go#L61-L97","documentation":"setupOTel wraps the error from runtime.Start, which registers the Go runtime metrics (GC, memory, goroutines) with the given MeterProvider. Start fails only when called twice on the same provider or in an unsupported state; the wrap is \"runtime metrics: %w\".","triggerScenarios":"Calling runtime.Start(runtime.WithMeterProvider(mp)) when runtime instrumentation was already started (double setupOTel invocation), since the runtime package only supports a single registration.","commonSituations":"Calling setupOTel twice (e.g. in init and main), or multiple importers each starting runtime metrics on their own provider.","solutions":["Call setupOTel (and thus runtime.Start) exactly once per process.","Use sync.Once around setupOTel to guarantee single initialization.","If multiple providers are needed, register runtime metrics only on the primary one.","Log the wrapped error; the message indicates double-start or internal registration failure."],"exampleFix":"// before\nif err := runtime.Start(runtime.WithMeterProvider(mp)); err != nil {\n    return nil, fmt.Errorf(\"runtime metrics: %w\", err)\n}\n// after\nvar setupOnce sync.Once\nfunc setupOTelOnce() error {\n    var err error\n    setupOnce.Do(func() { err = setupOTel() })\n    return err\n}","handlingStrategy":"try-catch","validationCode":"var otelOnce sync.Once\nvar otelErr error\nfunc ensureOTelStarted() error {\n    otelOnce.Do(func() { otelErr = setupOTel() })\n    return otelErr\n}","typeGuard":null,"tryCatchPattern":"if err := ensureOTelStarted(); err != nil {\n    log.Printf(\"runtime metrics not started: %v\", err)\n}","preventionTips":["Wrap setupOTel in sync.Once.","Never call runtime.Start twice with different providers.","Centralize telemetry initialization in one place (e.g. main)."],"tags":["go","opentelemetry","metrics","runtime"],"backgroundTag":"invalid-state-transition","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}