[BUG] OTEL_EXPORTER_OTLP_HEADERS is not applied to the traces exporter (enhanced telemetry beta)

Status Fixed / completed
Reported on v2.1.221
Maintainer reply None cached
Activity 1 comment · opened Aug 5, 2026 · closed Aug 5, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

With the enhanced telemetry beta enabled, spans are exported over OTLP but the Authorization header from OTEL_EXPORTER_OTLP_HEADERS is never attached to the request.

Any backend that requires authentication rejects the export. In my case that is a self-hosted Langfuse instance, which uses HTTP Basic auth on its OTLP endpoint — it returns 401 and the spans are silently dropped. There is no error surfaced by the CLI, so the failure looks identical to "tracing is not working at all".

I confirmed the header is missing by pointing the exporter at a local server that logs incoming request headers. Spans arrive (2043 bytes, correct claude_code.* span names and gen_ai.* attributes), but no Authorization header is present.

I tried four variations, all with the same result:

| Variation | Result |
|---|---|
| OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <base64>" | no Authorization header |
| OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Basic <base64>" (per-signal) | no Authorization header |
| Value percent-encoded: Authorization=Basic%20<base64> | no Authorization header |
| Combined with OTEL_EXPORTER_OTLP_TRACES_ENDPOINT instead of the common endpoint | no Authorization header |

What Should Happen?

The documentation states that traces use the common OTLP configuration:

Traces reuse the common OTLP configuration for endpoint, protocol, headers, and mTLS.

So the POST /v1/traces request should carry the Authorization header built from OTEL_EXPORTER_OTLP_HEADERS, the same way it is documented for metrics and logs.

Error Messages/Logs

Receiving server output (headers logged verbatim, Authorization absent):

POST /v1/traces bytes=2043
  Content-Type: application/x-protobuf
  User-Agent: OTel-OTLP-Exporter-JavaScript/0.208.0

For comparison, the same endpoint reached with curl and the identical Basic credential returns 200, and without it returns 401 — so the credential itself and the endpoint are correct:

$ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:3200/api/public/otel/v1/traces \
    -u "pk-...:sk-..." -H "Content-Type: application/x-protobuf" --data-binary ""
200
$ curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:3200/api/public/otel/v1/traces \
    -H "Content-Type: application/x-protobuf" --data-binary ""
401

Steps to Reproduce

  1. Save a server that logs request headers and listens on 127.0.0.1:4318:
from http.server import BaseHTTPRequestHandler, HTTPServer

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get("Content-Length", 0))
        self.rfile.read(length)
        print(f"POST {self.path} bytes={length}", flush=True)
        for name in ("Content-Type", "Authorization", "User-Agent"):
            if self.headers.get(name):
                print(f"  {name}: {self.headers[name]}", flush=True)
        self.send_response(200)
        self.end_headers()

    def log_message(self, *args):
        pass

HTTPServer(("127.0.0.1", 4318), Handler).serve_forever()
  1. Run it, then run Claude Code with tracing pointed at it:
CLAUDE_CODE_ENABLE_TELEMETRY=1 \
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 \
OTEL_TRACES_EXPORTER=otlp \
OTEL_METRICS_EXPORTER=none \
OTEL_LOGS_EXPORTER=none \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic dGVzdDp0ZXN0" \
claude -p "Reply with exactly one word: pong"
  1. The server logs the POST /v1/traces with Content-Type and User-Agent, but no Authorization.

Environment

  • Claude Code 2.1.221
  • macOS 24.6.0 (arm64)

Is this a regression?

I don't know — I have only used the traces beta on this version.

Additional context

A workaround is to put an OpenTelemetry Collector in front and let it attach the credential (otlphttp exporter with a headers: block). That works, and I verified it end to end. But it means every deployment that exports traces to an authenticated backend needs an extra process, which the documented configuration implies is unnecessary.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗