[BUG] OTLP logs export regressed from Content-Length to Transfer-Encoding: chunked in 2.1.191 — breaks Content-Length-requiring OTLP/HTTP endpoints
Summary
Between Claude Code 2.1.190 and 2.1.191, the OTLP/HTTP logs exporter changed from sending Content-Length to Transfer-Encoding: chunked. The bundled exporter version (OTel-OTLP-Exporter-JavaScript/0.208.0) and the emitted events are unchanged — only the HTTP body framing flipped. OTLP ingestion endpoints that require Content-Length now reject every export, silently.
Environment
- Claude Code 2.1.191 – 2.1.197 (current)
CLAUDE_CODE_ENABLE_TELEMETRY=1,OTEL_LOGS_EXPORTER=otlp,OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf- Target: a
Content-Length-requiring OTLP/HTTP ingestion endpoint (common for managed/enterprise collectors and cloud ingestion gateways) — e.g. Azure Monitor Data Collection Endpoints.
What breaks
On 2.1.191+ the export request is sent with Transfer-Encoding: chunked and no Content-Length. Endpoints that require Content-Length reject it. Azure Monitor's DCE returns:
HTTP 400 {"code":"MissingContentLengthHeader","message":"Invalid request. Header 'Content-Length' must be specified."}
The identical payload sent with a Content-Length header returns 204.
Regression bisect
Each published binary run against a local capture endpoint:
| Version | Published | Body framing | Strict endpoint |
|---|---|---|---|
| 2.1.185 | 2026-06-20 | Content-Length | ✅ 204 |
| 2.1.190 | 2026-06-24 | Content-Length | ✅ 204 — last good |
| 2.1.191 | 2026-06-24 | chunked | ❌ 400 — first broken |
| 2.1.193 / 2.1.195 | 2026-06-25 / 26 | chunked | ❌ 400 |
All bundle the same OTel-OTLP-Exporter-JavaScript/0.208.0 and emit identical events (api_request, claude_code.*) — so the change is in Claude Code's bundle, not the OpenTelemetry dependency version.
Impact
We ingest Claude Code's OTLP api_request events into Azure Monitor for usage/cost reporting. The 2.1.191 update silently stopped telemetry from landing, and with no client-side error it wasn't obvious where the break was — we chased server-side and network layers before capturing the raw emission and bisecting to the exact version.
Two gaps compounded it: the framing regression (this bug), plus the endpoint's 400 being swallowed with no log — a total outage looks identical to "working." A single warning on a non-2xx OTLP response would make this class of failure obvious.
Reproduction
- Configure the OTLP logs exporter (above), pointing
OTEL_EXPORTER_OTLP_LOGS_ENDPOINTat a local HTTP server that logs request headers. - Run any command; inspect the captured request.
- On 2.1.190:
Content-Length: <N>. On 2.1.191+:Transfer-Encoding: chunked, noContent-Length.
Note (pre-empting "chunked is valid HTTP")
Both framings are valid HTTP/1.1. But the OTLP request body is fully buffered before send, so its length is already known — Content-Length is the correct, more interoperable choice: it works with both lenient and strict endpoints, whereas chunked breaks the strict class for no benefit. 2.1.190 did exactly this; 2.1.191 regressed it.
Suggested fix
Restore Content-Length on the OTLP/HTTP logs request (set the header / end(buffer) on the fully-buffered payload instead of streaming it). Optionally, surface non-2xx OTLP export responses in a log line so silent drops are detectable.
12 Comments
Reproduced on 2.1.201 (Linux, native install) against a self-hosted OpenTelemetry Collector behind a Cilium/Envoy ingress — with evidence the 411 is generated client-side before any network I/O.
Setup
Telemetry env delivered via
/etc/claude-code/managed-settings.json:Endpoint is an Envoy ingress (Cilium) →
otel/opentelemetry-collector-contribOTLP HTTP receiver (4318).Observed
Every export attempt fails with 411. From
claude --debug -p "ok":Key finding: the 411 occurs before any network I/O
We pointed
OTEL_EXPORTER_OTLP_ENDPOINTat a raw localhost socket listener and re-ran with--debug. The CLI reported the identicalOTLPExporterError: Length Required (411)while the listener received zero connections. The error appears to be fabricated inside the CLI/Bun HTTP stack — no request is ever transmitted, so this is not an endpoint/proxy compatibility problem.Consistent with that, the server side is provably tolerant: the same Envoy ingress + collector accept every hand-crafted variant we sent (valid
Transfer-Encoding: chunkedfor JSON and protobuf, gzip + chunked,Content-Length+ chunked combined, even a malformed POST with a body but neitherContent-LengthnorTransfer-Encoding). We could not produce a 411 from the server with any request shape — only the CLI's exporter reports one.Other data points
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufandhttp/json.OTEL_EXPORTER_OTLP_COMPRESSION=none.OTEL_EXPORTER_OTLP_PROTOCOL=grpcfails differently:14 UNAVAILABLE: No connection established. Last error: h2 is not supported— the Bun runtime has no HTTP/2 client, so the gRPC path is also unusable. On 2.1.191+ every OTLP export path is dead client-side.npx @anthropic-ai/claude-code@2.1.201produces the identical error with the same/$bunfs/stack — it ships the same Bun-compiled binary.--debug; otherwise the failure is completely silent.Happy to provide raw captures or run candidate builds against this setup.
Reproduced on 2.1.202 (Windows, Claude apps gateway setup). We are evaluating the claude gateway for company-wide adoption, which requires ≥2.1.195, so reverting to 2.1.190 is not an option. Same debug log pattern as above. Please prioritize.
Here's output from --debug log:
Still broken as of the 2.1.206 release as well.
Lots of "fixes" to 3P related things in 2.1.207, but this is still the same error.
In version 2.1.207 telemetry from client to gateway does not work.
Still broken in 2.1.210.
Re-verified empirically today: pointed the
http/protobuflogs exporter (OTEL_LOGS_EXPORTER=otlp,OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf) at a local header-capture server and ran aclaude -psession. Every export still arrives as:Bundled exporter version unchanged (0.208.0), so this remains the body-framing flip introduced in 2.1.191 — Content-Length-requiring OTLP endpoints (e.g. Azure Monitor DCE, which responds
400 MissingContentLengthHeader) still reject every export, silently.This was closed on 2026-07-15 without a linked fix or comment, but the regression is still present in the latest release, 2.1.211 (current npm latest, and its changelog doesn't mention this issue).
Re-verified empirically just now, same method as my 2.1.210 report:
http/protobuflogs exporter pointed at a local header-capture server, three exports captured — all three arrive withtransfer-encoding: chunkedand noContent-Length, so Content-Length-requiring OTLP endpoints (Azure Monitor DCE et al.) still reject every batch.If the fix is merged but unreleased, could you note the target version here? Otherwise, please reopen — happy to re-verify against any build.
Agreed. Telemetry is an active problem for anyone trying to use 3P right now; in both Code and Desktop deployments. This is basically the reliable method for getting per-user metrics and other information, but we can't use it right now. We have also been testing every release looking for this fix.
@bhosmer-ant Why was the ticket closed? Unfortunately, the problem persists in version 2.1.211
Confirmed fixed in 2.1.212 ✅ — verified with the same header-capture method used earlier in this thread (nested
claude -pwith an OTLP logs endpoint repointed at a local capture server via--settings).Fix #1 — Content-Length regression: the OTLP/HTTP logs export now sends a proper
Content-Lengthand noTransfer-Encoding: chunked:That unblocks Azure Monitor DCE and other endpoints that reject chunked OTLP/HTTP (the
MissingContentLengthHeader400 is gone).Fix #2 — missing
trace_id/span_idwithTRACEPARENT: also confirmed — withTRACEPARENTset in headless/-pmode, the exported log records now carry the expectedtrace_idandspan_id(both traceparent id bytes present in the captured payload).Thanks for landing both — we can now retire the local Content-Length forwarder shim we'd been running as a workaround. 🙏
I'll do even better here... we just confirmed that this also works with claude-apps-gateway and telemetry: forward_to:. This fix makes the claude-apps-gateway a viable path to get per-user data, etc. now; because it also tags the user information from it's authentication cache.