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.

Best practices
OpenTelemetry Not Showing Traces? The 7-Step Debug Checklist (2026)

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 (and baggage if used) in nginx/Envoy/ALB configs.
  • Mismatched propagators — one service emits W3C, another expects B3. Set explicitly: OTEL_PROPAGATORS=tracecontext,baggage on 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)

  1. debug/console exporter: are spans produced at all?
  2. nc -zv to 4317/4318 from the app's network namespace.
  3. Collector logs: auth/TLS/transport errors?
  4. traces pipeline declared, receivers/exporters wired, no forgotten filter?
  5. Propagators uniform (tracecontext,baggage); headers survive proxies?
  6. Sampler forced to always_on for the test?
  7. 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.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting