Skip to content

Errors

The two things that can fail at construction

tracing.NewProvider, metrics.NewProvider and logs.NewProvider return an error from exactly two places:

  1. Endpoint validationotelcore.ParseEndpoint rejected Settings.Endpoint. Wraps otelcore.ErrInvalidEndpoint.
  2. Exporter construction — the OTLP/HTTP exporter constructor failed. Wrapped with creating OTLP trace exporter, creating OTLP metric exporter or creating OTLP log exporter.

Everything else — an unreachable collector, a rejected batch, an authentication failure — happens later, asynchronously, and is not returned to you.

ErrInvalidEndpoint

var ErrInvalidEndpoint = errors.New("invalid OTLP endpoint")

Every validation failure wraps this sentinel, so a caller can separate a misconfiguration from a transport problem:

tp, err := tracing.NewProvider(ctx, res, settings)
if errors.Is(err, otelcore.ErrInvalidEndpoint) {
    // The operator's configuration is wrong. Fail startup with a clear message.
}

Errors are created with github.com/cockroachdb/errors, and the specific reason is carried as a hint rather than in the message. errors.GetAllHints(err) retrieves it; %+v prints it. The endpoint itself is deliberately never included in the error text, because a rejected endpoint may carry the credentials that got it rejected.

Every endpoint rejection

Checked in this order. The first failure returns.

Input Hint Why
"" OTLP endpoint must not be empty Only reachable by calling ParseEndpoint directly. NewProvider treats an empty Settings.Endpoint as the env-fallback path and never calls it.
Longer than 2048 bytes OTLP endpoint is N bytes; max is 2048 otelcore.MaxEndpointLength. Real collector URLs are well under 200 bytes.
Contains a byte below 0x20, or 0x7F OTLP endpoint contains ASCII control characters Blocks header and log injection through a newline or a carriage return in a configured value.
Unparseable as a URL — https://host/%zz, https://[::1 (no hint) The message is parsing OTLP endpoint URL: invalid OTLP endpoint, with no echo of the input, because a parse error can quote attacker-influenced text. Note that a newline never reaches here: the control-character check above catches it first.
https://user:pass@host or https://user@host OTLP endpoint must not contain credentials; use headers instead Any userinfo is rejected, with or without a password. Put the credential in Settings.Headers.
grpc://host:4317, ftp://host, unix://… OTLP endpoint scheme "grpc" is not supported; use http or https This module builds OTLP/HTTP exporters only.
collector:4318 (no scheme) OTLP endpoint scheme "collector" is not supported; use http or https url.Parse reads a bare host:port as scheme collector with opaque 4318. Write https://collector:4318.
//collector:4318 OTLP endpoint scheme "" is not supported; use http or https A scheme-relative URL has no scheme to allow.
https:// (no host) OTLP endpoint must include a host

Two inputs that look wrong and are accepted:

  • A trailing slash. https://collector:4318/ is normalised — the path is trimmed before the per-signal suffix is appended, so the request goes to /v1/traces and not //v1/traces, which strict collectors answer with a 404.
  • A base path. https://collector:4318/otlp is a supported shape; the signal suffix is appended to it, giving /otlp/v1/traces.

When endpoint validation runs

Only when Settings.Endpoint is non-empty. On the env-fallback path — an empty Endpoint — no validation happens here at all, and a malformed OTEL_EXPORTER_OTLP_ENDPOINT is the SDK's problem rather than this module's. The hardening described above protects values that came from your configuration, not values that came from the environment.

Exporter-construction failures

The OTLP/HTTP exporter constructors are strict about one combination in particular: a TLS configuration together with an insecure endpoint. Supplying OTEL_EXPORTER_OTLP_CERTIFICATE while the endpoint resolves to plaintext — an http:// scheme, Settings.Insecure: true, or OTEL_EXPORTER_OTLP_INSECURE=true — fails at construction:

creating OTLP trace exporter: insecure HTTP endpoint cannot use TLS client configuration

This is the failure the module's exporter_error_test.go files exercise, and it needs neither a network nor a collector to reproduce.

Malformed environment values are handled differently: an unparseable OTEL_EXPORTER_OTLP_TIMEOUT, a header without an =, or an unreadable certificate file is written to the OTel internal logger and the default is used. Construction still succeeds. Nothing surfaces unless you have called otel.SetLogger with a logger that records at that verbosity.

Failures that are never returned

NewProvider returning nil means the provider was built. It does not mean the collector exists, is reachable, or will accept anything you send it. Exporting is asynchronous, and these all fail after construction has succeeded:

Failure What you see
Collector unreachable, DNS failure, connection refused Retried for up to 60 seconds, then the batch is dropped. Reported through the OTel global error handler.
HTTP 401/403 from a missing or wrong header Same — retried where the status allows, then dropped
A 404 from a wrong base path Same
Queue full because export is slower than production Spans and records are dropped silently at enqueue

To see any of it, install a handler before building providers:

otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
    slog.Error("otel export", "err", err)
}))

Without one, the SDK writes to its own logger and a service that cannot reach its collector looks exactly like a service with nothing to report. The missing-telemetry checklist works through it.