caddyserver/caddy · error
creating trace exporter error: %w
Error message
creating trace exporter error: %w
What it means
Thrown by the Caddy HTTP tracing module while provisioning when autoexport.NewSpanExporter(ctx) fails to build an OpenTelemetry span exporter. autoexport picks the exporter from OTEL_TRACES_EXPORTER and the wire protocol from OTEL_EXPORTER_OTLP_PROTOCOL, so an unknown exporter/protocol name, an exporter not compiled into the binary, or an invalid OTLP endpoint causes this. The underlying cause is wrapped with %w, so the chained error names the exact problem.
Source
Thrown at modules/caddyhttp/tracing/tracer.go:75
}
version, _ := caddy.Version()
res, err := ot.newResource(caddyhttp.ServerHeader, version)
if err != nil {
return ot, fmt.Errorf("creating resource error: %w", err)
}
ot.propagators = autoprop.NewTextMapPropagator()
// Defer creation of the exporter (and its batch span processor goroutine)
// until we know a new provider is actually needed. When the global provider
// already exists it is reused and these options are discarded; building them
// here unconditionally would leak the exporter and a BatchSpanProcessor
// goroutine on every config reload.
tracerProvider, err := globalTracerProvider.getTracerProvider(func() ([]sdktrace.TracerProviderOption, error) {
traceExporter, err := autoexport.NewSpanExporter(ctx)
if err != nil {
return nil, fmt.Errorf("creating trace exporter error: %w", err)
}
return []sdktrace.TracerProviderOption{
sdktrace.WithBatcher(traceExporter),
sdktrace.WithResource(res),
}, nil
})
if err != nil {
return ot, err
}
ot.handler = otelhttp.NewHandler(http.HandlerFunc(ot.serveHTTP),
ot.spanName,
otelhttp.WithTracerProvider(tracerProvider),
otelhttp.WithPropagators(ot.propagators),
otelhttp.WithSpanNameFormatter(ot.spanNameFormatter),
)
View on GitHub (pinned to 50e54ee279)
Solutions
- Inspect the wrapped cause in the log line `creating trace exporter error:` — autoexport errors name the offending env var (e.g. `invalid value for environment variable "OTEL_EXPORTER_OTLP_PROTOCOL"`)
- Set OTEL_EXPORTER_OTLP_PROTOCOL to a supported value: grpc, http/protobuf, or http/json (and make sure the matching exporter is compiled in)
- Set OTEL_TRACES_EXPORTER to otlp (or unset it; otlp is the default) and verify OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_TRACES_OTLP_ENDPOINT is a full URL like https://collector:4318
- If no collector is intended, remove the `tracing` directive or set OTEL_TRACES_EXPORTER=none where supported
- After fixing env vars, fully restart Caddy (config reload reuses a global provider and may keep the old state)
Example fix
# before (unit file / container env) OTEL_EXPORTER_OTLP_PROTOCOL=http/proto OTEL_TRACES_EXPORTER=otlp-grpc # after OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_TRACES_EXPORTER=otlp OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
Defensive patterns
Strategy: validation
Validate before calling
// Before enabling the tracing directive, verify exporter env config:
func checkExporterEnv() error {
if p := os.Getenv("OTEL_EXPORTER_OTLP_PROTOCOL"); p != "" {
switch p {
case "grpc", "http/protobuf", "http/json":
default:
return fmt.Errorf("unsupported OTEL_EXPORTER_OTLP_PROTOCOL %q", p)
}
}
if e := os.Getenv("OTEL_TRACES_EXPORTER"); e != "" && e != "otlp" && e != "none" {
return fmt.Errorf("unsupported OTEL_TRACES_EXPORTER %q", e)
}
if ep := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT"); ep != "" {
if _, err := url.Parse(ep); err != nil {
return fmt.Errorf("bad OTEL_EXPORTER_OTLP_ENDPOINT: %w", err)
}
}
return nil
} Try / catch
// The error is returned from Provision; catch it at config load and log the wrapped cause:
if err := tracerModule.Provision(ctx); err != nil {
if strings.Contains(err.Error(), "creating trace exporter error") {
log.Printf("tracing disabled: fix OTEL env vars: %v", err)
}
return err
} Prevention
- Validate OTEL_* env vars in the container/unit-file template before deploying Caddy
- Pin OTEL_EXPORTER_OTLP_PROTOCOL explicitly to http/protobuf in managed deployments
- Run `caddy validate --config` in CI with the same env vars the service runs with
When it happens
Trigger: Running Caddy with the `tracing` directive while OTEL_TRACES_EXPORTER is set to an unregistered name (not otlp/console/none), or OTEL_EXPORTER_OTLP_PROTOCOL is set to grpc without the gRPC exporter available, or OTEL_EXPORTER_OTLP_ENDPOINT/OTEL_TRACES_OTLP_ENDPOINT has a malformed URL (missing scheme, bad port). autoexport.NewSpanExporter(ctx) returns a non-nil error and the wrapper fires at modules/caddyhttp/tracing/tracer.go:75.
Common situations: Env vars copied from another OTel-instrumented service into the Caddy unit file or container; protocol set to `grpc` when the OTLP receiver only accepts HTTP protobuf (or the grpc plugin tag was not built); typos like `http/proto` instead of `http/protobuf`; an empty OTEL_EXPORTER_OTLP_ENDPOINT after an env refactor.
Related errors
- creating resource error: %w
- replacing listen address: %v
- creating OTLP metric reader: %w
- building OTLP metrics resource: %w
- tracerProvider shutdown: %w
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/a758e8c224650b03.
Report an issue: GitHub.