OpenTelemetry Not Showing Traces? The 7-Step Debug Checklist (2026)
OpenTelemetry traces missing? Debug in 7 steps — debug exporter, endpoint/TLS checks, sampling traps, context propagation, and the Collector logs that name the cause.
Short answer: When OpenTelemetry traces don't appear, the cause is almost always one of seven things, in this frequency order: (1) sampling set to zero or dropping everything, (2) wrong exporter endpoint or port (4317 gRPC vs 4318 HTTP), (3) authentication/TLS failure to the backend, (4) the Collector pipeline doesn't include traces, (5) context propagation broken between services, (6) resource attributes missing so the backend drops or misfiles data, (7) the app never started the SDK. Work the checklist below; step 1 (the debug exporter) resolves most cases in minutes.
Step 1 — Prove the app is producing spans (debug exporter)
Before blaming the network, make the SDK print what it produces:
# Collector: temporary debug/stdout sink
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug] # bypass the real backend entirely
Or SDK-side (every language has a console/stdout exporter — e.g., Java's OTEL_TRACES_EXPORTER=logging, Python's ConsoleSpanExporter). Spans in the console → your app is fine; the break is downstream (steps 2–5). No spans → sampling or SDK init (steps 6–7).
Step 2 — Endpoint and port: the 4317/4318 classic
OTLP has two transports and they are not interchangeable:
Related guideDatadog to OpenTelemetry: The 2026 Migration Playbook (Without Losing Visibility)→
| Transport | Default port | Common mistake |
|---|---|---|
| gRPC | 4317 | Pointing an HTTP client at it (or vice versa) |
| HTTP/protobuf | 4318 | Forgetting the path (/v1/traces) in some SDKs |
# Is anything listening?
nc -zv otel-collector 4317
# From the app container/pod — not your laptop:
kubectl exec -it <app-pod> -- nc -zv otel-collector.observability 4317
A very common Kubernetes failure: the app targets localhost:4317 but the Collector runs as a DaemonSet on the node — use status.hostIP via the downward API, or the Collector Service name, not localhost.
Step 3 — Auth and TLS: read the Collector's own log
kubectl logs -l app=otel-collector --tail=100 | grep -iE "error|refused|denied|tls"
Signatures: connection refused (endpoint), certificate signed by not publicly documented authority (TLS trust), 401/403 (token), Permanent error: ... unsupported protocol (transport mismatch). One quiet killer: backends that require a path prefix (https://gateway.example.com/otlp) — some SDKs strip or append paths differently; test with curl against the exact URL.
Step 4 — The pipeline actually includes traces
Collector configs fail silently when a pipeline is miswired:
service:
pipelines:
traces: # ← must exist AND be named exactly
receivers: [otlp] # the receiver name must match the receivers: block
processors: [batch]
exporters: [otlphttp/backend]
otelcol validate --config=config.yaml (or the dry-run flag for your distribution) catches structural errors; it does not catch a pipeline you simply forgot to declare. Also check for a filter/probabilistic_sampler processor someone added and forgot.
Step 5 — Context propagation: traces exist but arrive broken
Symptom: you see spans, but each service's spans form isolated one-span "traces" instead of a tree. The traceparent header is being dropped between services. Causes and fixes:
- Proxies/gateways stripping headers — allowlist
traceparent/tracestate(andbaggageif used) in nginx/Envoy/ALB configs. - Mismatched propagators — one service emits W3C, another expects B3. Set explicitly:
OTEL_PROPAGATORS=tracecontext,baggageon every service. - Messaging hops — queues don't propagate headers automatically; inject context into message attributes (OTel messaging semantic conventions).
Step 6 — Sampling: the silent zero
Sampling misconfiguration discards data before any network is involved:
Related guidePrometheus Monitoring: The Complete 2026 Guide→
# Check the effective settings
OTEL_TRACES_SAMPLER=parentbased_always_on # safe default for debugging
OTEL_TRACES_SAMPLER_ARG=1.0
Traps: an inherited OTEL_TRACES_SAMPLER=parentbased_traceidratio with ARG=0 from an old staging config; a Collector probabilistic_sampler set to 0.01 from a cost exercise; head-based sampling on the client dropping spans the Collector never sees. For debugging, force always_on end to end, confirm visibility, then restore sane sampling.
Step 7 — SDK never initialized
The embarrassingly common one: auto-instrumentation not actually attached. Java: the -javaagent flag didn't survive a Dockerfile refactor (ps aux | grep javaagent). Node: --require runs after the framework loads (auto-instrumentation must preload first). Python: forgot opentelemetry-instrument wrapper. Confirm by looking for the SDK's startup log line — every OTel SDK logs its initialization at INFO.
The compressed checklist (print this)
debug/console exporter: are spans produced at all?nc -zvto 4317/4318 from the app's network namespace.- Collector logs: auth/TLS/transport errors?
tracespipeline declared, receivers/exporters wired, no forgotten filter?- Propagators uniform (
tracecontext,baggage); headers survive proxies? - Sampler forced to
always_onfor the test? - Agent/SDK actually attached (startup log line present)?
FAQ
Q: Why are my OpenTelemetry traces not showing up in the backend?
Most often: sampling dropping everything (check OTEL_TRACES_SAMPLER), the wrong OTLP endpoint/port (4317 gRPC vs 4318 HTTP), or an auth/TLS failure visible in the Collector logs. Prove the app produces spans first with a console/debug exporter — that splits the problem into "app side" vs "pipeline side" immediately.
Q: How do I debug the OpenTelemetry Collector?
Add a debug exporter (formerly logging exporter) with verbosity: detailed to the traces pipeline — it prints every received span to the Collector log. Combined with otelcol validate --config=... for config structure and the Collector's own error logs for backend failures, this isolates any pipeline break in minutes.
Q: What is the default OpenTelemetry port?
OTLP/gRPC defaults to 4317; OTLP/HTTP defaults to 4318 (with paths like /v1/traces). A large share of "no traces" incidents are transport mismatches — an HTTP exporter pointed at a gRPC port or vice versa. Check both the SDK's OTEL_EXPORTER_OTLP_ENDPOINT and the Collector receiver's protocols block.
Q: Why do I see spans but they're not connected into traces?
Context propagation is broken: the traceparent header isn't surviving the journey between services. Uniformly set OTEL_PROPAGATORS=tracecontext,baggage, ensure proxies and API gateways forward those headers, and for message queues inject context into message attributes — queue hops don't propagate automatically.
Q: Does sampling hide traces?
Yes — by design. Head-based sampling (e.g., parentbased_traceidratio at 0.1) discards 90% of traces at the source, and a misconfigured ARG=0 discards everything. During debugging, set OTEL_TRACES_SAMPLER=always_on at the SDK and remove Collector-side sampler processors, confirm full visibility, then reintroduce sampling deliberately.
Sources: OpenTelemetry documentation (Collector configuration, OTLP specification, propagators, sampling). Verified 2026-08-07.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan