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

  1. 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"`)
  2. Set OTEL_EXPORTER_OTLP_PROTOCOL to a supported value: grpc, http/protobuf, or http/json (and make sure the matching exporter is compiled in)
  3. 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
  4. If no collector is intended, remove the `tracing` directive or set OTEL_TRACES_EXPORTER=none where supported
  5. 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

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


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/a758e8c224650b03. Report an issue: GitHub.