microsoft/aspire · error
runtime metrics
Error message
runtime metrics: %w
What it means
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".
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.
Example fix
// before
if err := runtime.Start(runtime.WithMeterProvider(mp)); err != nil {
return nil, fmt.Errorf("runtime metrics: %w", err)
}
// after
var setupOnce sync.Once
func setupOTelOnce() error {
var err error
setupOnce.Do(func() { err = setupOTel() })
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
var otelOnce sync.Once
var otelErr error
func ensureOTelStarted() error {
otelOnce.Do(func() { otelErr = setupOTel() })
return otelErr
} Try / catch
if err := ensureOTelStarted(); err != nil {
log.Printf("runtime metrics not started: %v", err)
} Prevention
- Wrap setupOTel in sync.Once.
- Never call runtime.Start twice with different providers.
- Centralize telemetry initialization in one place (e.g. main).
When it happens
Trigger: Calling runtime.Start(runtime.WithMeterProvider(mp)) when runtime instrumentation was already started (double setupOTel invocation), since the runtime package only supports a single registration.
Common situations: Calling setupOTel twice (e.g. in init and main), or multiple importers each starting runtime metrics on their own provider.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- metric exporter
- Histogram data point has bucket counts without any explicit…
- log exporter
- Metric data point value must be finite.
- otel resource
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/6d3900e02797cc55.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Templating/Templates/go-starter/api/telemetry.go:79
)
otel.SetTracerProvider(tp)
metricOpts := []otlpmetricgrpc.Option{}
if len(headers) > 0 {
metricOpts = append(metricOpts, otlpmetricgrpc.WithHeaders(headers))
}
metricExp, err := otlpmetricgrpc.New(ctx, metricOpts...)
if err != nil {
return nil, fmt.Errorf("metric exporter: %w", err)
}
mp := sdkmetric.NewMeterProvider(
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExp)),
sdkmetric.WithResource(res),
)
otel.SetMeterProvider(mp)
if err := runtime.Start(runtime.WithMeterProvider(mp)); err != nil {
return nil, fmt.Errorf("runtime metrics: %w", err)
}
logOpts := []otlploggrpc.Option{}
if len(headers) > 0 {
logOpts = append(logOpts, otlploggrpc.WithHeaders(headers))
}
logExp, err := otlploggrpc.New(ctx, logOpts...)
if err != nil {
return nil, fmt.Errorf("log exporter: %w", err)
}
lp := sdklog.NewLoggerProvider(
sdklog.WithProcessor(sdklog.NewBatchProcessor(logExp)),
sdklog.WithResource(res),
)
otellog.SetLoggerProvider(lp)
// Route slog to OTel so structured logs appear in the dashboard.
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil)))View on GitHub (pinned to 25830f84bd)