caddyserver/caddy · error
creating OTLP metric reader: %w
Error message
creating OTLP metric reader: %w
What it means
When metrics are configured for export (metrics { ... } with OTLP/autoexport), AdminMetrics/InstrumentationMetrics provisioning calls autoexport.NewMetricReader(ctx), which reads OTEL_METRICS_EXPORTER and related OTEL_EXPORTER_OTLP_* environment variables. Failures — unknown exporter name, no exporter available for the requested protocol — are wrapped as 'creating OTLP metric reader'. Note the code first installs a fallback producer reading Caddy's own Prometheus registry; OTEL_METRICS_PRODUCERS can override it.
Source
Thrown at modules/caddyhttp/metrics.go:193
}
// Register a Prometheus -> OpenTelemetry bridge against the process-wide
// Prometheus registry as the *default* source the NewMetricReader below
// will read from.
//
// NB: despite the "With*" naming, autoexport.WithFallbackMetricProducer is
// a package-level setter (it returns nothing) — it mutates autoexport's
// internal producer registry and takes effect on the very next call to
// NewMetricReader. It is NOT a MetricOption and must not be passed as one.
// Users can still override the source by setting OTEL_METRICS_PRODUCERS.
reg := ctx.GetMetricsRegistry()
autoexport.WithFallbackMetricProducer(func(context.Context) (sdkmetric.Producer, error) {
return otelprom.NewMetricProducer(otelprom.WithGatherer(reg)), nil
})
reader, err := autoexport.NewMetricReader(ctx)
if err != nil {
return fmt.Errorf("creating OTLP metric reader: %w", err)
}
version, _ := caddy.Version()
res, err := resource.Merge(resource.Default(), resource.NewSchemaless(
semconv.WebEngineName(ServerHeader),
semconv.WebEngineVersion(version),
))
if err != nil {
return fmt.Errorf("building OTLP metrics resource: %w", err)
}
m.meterProvider = sdkmetric.NewMeterProvider(
sdkmetric.WithResource(res),
sdkmetric.WithReader(reader),
)
return nil
}View on GitHub (pinned to 50e54ee279)
Solutions
- Check OTEL_METRICS_EXPORTER — use a supported value (e.g. otlp, prometheus, none per your OTel SDK version) or unset it.
- Verify OTEL_EXPORTER_OTLP_PROTOCOL / OTEL_EXPORTER_OTLP_METRICS_PROTOCOL match a protocol the build supports (grpc vs http/protobuf).
- Unset conflicting OTEL_* variables in the service unit/entrypoint (env -u OTEL_METRICS_EXPORTER ...) if OTLP export was never intended.
- Confirm the endpoint env (OTEL_EXPORTER_OTLP_ENDPOINT / _METRICS_ENDPOINT) is reachable and well-formed.
Example fix
# before export OTEL_METRICS_EXPORTER=otlpgrp # after export OTEL_METRICS_EXPORTER=otlp export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
Defensive patterns
Strategy: validation
Validate before calling
# Shell: verify exporter env before starting Caddy export OTEL_METRICS_EXPORTER=otlp # or prometheus / none, per your OTel build export OTEL_EXPORTER_OTLP_PROTOCOL=grpc env | grep '^OTEL_' # audit every variable for typos
Prevention
- Audit OTEL_* env vars in the service unit or container spec.
- Unset OTel variables in environments that only want Prometheus scraping.
- Keep endpoint and protocol variables consistent (_METRICS_PROTOCOL overrides _PROTOCOL).
When it happens
Trigger: OTEL_METRICS_EXPORTER set to an unsupported value (e.g. 'otlp' when the build lacks it, or a typo like 'otlpgrp'); OTEL_EXPORTER_OTLP_PROTOCOL/OTEL_EXPORTER_OTLP_METRICS_PROTOCOL set to an unregistered protocol (e.g. 'http/protobuf' in a build without it); env vars set globally leaking into the Caddy process.
Common situations: Kubernetes pods inheriring OpenTelemetry operator sidecar env vars; CI shells with stray OTEL_* exports; upgrading OTel libraries that renamed valid exporter values; enabling `metrics` blocks on servers where ops intended Prometheus scrape only.
Related errors
- building OTLP metrics resource: %w
- creating resource error: %w
- no metrics registry found
- replacing listen address: %v
- parsing environment file: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/eee99ecb71c68a41.
Report an issue: GitHub.