[BUG] OTEL_METRICS_EXPORTER=otlp silently no-ops in 2.1.113 — OTLP exporter packages not bundled
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Setting OTEL_METRICS_EXPORTER=otlp with any OTEL_EXPORTER_OTLP_PROTOCOL (both http/protobuf and http/json tried) produces zero network traffic. No error is surfaced. OTEL_METRICS_EXPORTER=console works correctly in the same process, so the OTel SDK initializes and metrics accumulate — they just never ship via OTLP.
The Bun-compiled binary ships only @opentelemetry/api + the console exporter; the @opentelemetry/exporter-metrics-otlp-* packages aren't bundled, and the dynamic require() for them at runtime fails silently inside a try / catch.
What Should Happen?
Expected behavior
Per https://code.claude.com/docs/en/monitoring-usage, setting:
CLAUDE_CODE_ENABLE_TELEMETRY=1
OTEL_METRICS_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=https://collector_url
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer token_value
should cause Claude Code to POST OTLP metric payloads to collector_url/v1/metrics at the configured export interval.
Actual behavior
Collector receives zero requests from the client.
lsof -p <claude-pid> -i -n during a session shows no connections to the collector IP.
No error is printed to stderr, no log file appears under ~/.claude/, OTEL_LOG_LEVEL=debug produces no output.
Swapping otlp → console in the same shell, same env, works perfectly and dumps full metrics to stdout.
Verified on the collector end: a synthetic curl -X POST https://collector_url/v1/metrics with the same bearer token returns HTTP 200 — so the collector side is healthy.
Error Messages/Logs
Steps to Reproduce
- Stand up any OTLP/HTTP receiver that prints on receipt. Any of:
otel/opentelemetry-collector-contrib with an otlp receiver and debug exporter,
a tiny Go/Node OTLP receiver stub,
or just ncat -lk 4318 and check it never receives a byte.
- Run Claude Code with OTLP configured:
CLAUDE_CODE_ENABLE_TELEMETRY=1 \
OTEL_METRICS_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
OTEL_METRIC_EXPORT_INTERVAL=1000 \
claude -p "say hi"
- Swap the exporter to console — confirm OTel IS initializing:
CLAUDE_CODE_ENABLE_TELEMETRY=1 \
OTEL_METRICS_EXPORTER=console \
OTEL_METRIC_EXPORT_INTERVAL=1000 \
claude -p "say hi"
Prints full claude_code.session.count / claude_code.cost.usage /
claude_code.token.usage data-point objects to stdout.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.113
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Root cause (hypothesis, with evidence)
The Bun-compiled binary does not include the OTLP exporter packages. The SDK's factory code dynamically require()s them at runtime, resolves to null/undefined, and a destructuring assignment throws — which is caught and swallowed.
Evidence — strings in the binary:
$ strings /Users/<me>/.local/share/claude/versions/2.1.113 \
| grep -oE '@opentelemetry/[a-z0-9/._-]+' | sort -u
@opentelemetry/api
Only the API package. No exporter packages.
Error strings baked into the binary (clearly caught, never surfaced):
Cannot destructure property 'OTLPMetricExporter' from null or undefined value
Cannot destructure property 'OTLPLogExporter' from null or undefined value
Cannot destructure property 'OTLPTraceExporter' from null or undefined value
Console exporter IS bundled (which is why it works):
$ strings /Users/<me>/.local/share/claude/versions/2.1.113 \
| grep -iE 'consolemetricexporter'
ConsoleMetricExporter
Installing the missing packages globally (npm i -g @opentelemetry/exporter-metrics-otlp-proto) and setting NODE_PATH=$(npm root -g) does not help — Bun-compiled binaries don't resolve externals at runtime the way a Node script would.
Minimal repro
- Stand up any OTLP/HTTP receiver that prints on receipt. Any of:
otel/opentelemetry-collector-contrib with an otlp receiver and debug exporter,
a tiny Go/Node OTLP receiver stub,
or just ncat -lk 4318 and check it never receives a byte.
- Run Claude Code with OTLP configured:
CLAUDE_CODE_ENABLE_TELEMETRY=1 \
OTEL_METRICS_EXPORTER=otlp \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
OTEL_METRIC_EXPORT_INTERVAL=1000 \
claude -p "say hi"
- Swap the exporter to console — confirm OTel IS initializing:
CLAUDE_CODE_ENABLE_TELEMETRY=1 \
OTEL_METRICS_EXPORTER=console \
OTEL_METRIC_EXPORT_INTERVAL=1000 \
claude -p "say hi"
Prints full claude_code.session.count / claude_code.cost.usage /
claude_code.token.usage data-point objects to stdout.
Impact
This breaks the entire "run your own OpenTelemetry collector" story documented at https://code.claude.com/docs/en/monitoring-usage. Orgs deploying Claude Code + a collector for cost/productivity observability currently have no way to get metrics in without downgrading (if an earlier bundled version existed) or falling back to the Admin-API pull.
Workaround
None on the client side. Orgs using Claude Code telemetry have to either:
Fall back to the Admin API's Claude Code usage endpoint (daily granularity only, no live view), or
Scrape console exporter output from stdout and re-emit it out-of-band (brittle).
Suggested fix
Bundle at least @opentelemetry/exporter-metrics-otlp-proto, @opentelemetry/exporter-metrics-otlp-http, and @opentelemetry/exporter-metrics-otlp-grpc into the compiled binary. If bundle size is a concern, gate on OTEL_METRICS_EXPORTER detection and fail loud instead of silent when the selected exporter isn't available — current silent failure is the worst outcome.
Showing cached comments. Read the full discussion on GitHub ↗
14 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
OTEL_METRICS_EXPORTER=otlpsilently no-oping in 2.1.113 — because the OTLP exporter packages are not bundled in the native binary — is the same class of regression as the HTTP MCP timeout config (#50289) and the plugin cache wipe (#48985): the native binary transition from 2.1.112 to 2.1.113 changed what Node.js packages are included in the bundle, and the OTLP exporter was not brought along.This is a significant observability regression for any organization that relies on OTEL metrics for production monitoring of Claude Code usage. The metrics export appears to work (no error) but produces no data — exactly the kind of silent failure that is hardest to catch in production.
The two-part fix:
@opentelemetry/exporter-metrics-otlp-grpc(for gRPC/protobuf) or@opentelemetry/exporter-metrics-otlp-http(for JSON over HTTP) to be included. These are not in the current bundle.OTEL_METRICS_EXPORTER=otlpis set but the OTLP package is not available, Claude Code should log an error:"OTLP metrics exporter configured but package not available — metrics will not be exported."Silent no-op is unacceptable for a monitoring configuration.Workaround while the fix is deployed: If you can use
OTEL_METRICS_EXPORTER=consoleinstead, metrics will be logged to stdout/stderr. Less useful for production but confirms OTEL is configured correctly.Related: #49893 (add OTEL effort tracking) and #50416 (expose full tool call details) — the overall OTEL observability for Claude Code is a work in progress, and this regression is a significant setback for it.
Confirming this on 2.1.111 and inside the Cowork VM — this is also the root cause of #39471
Re-ran @JuanNephrota's exact strings methodology against an older Cowork-bundled binary and against a live Cowork VM session. Two data points worth adding to this issue:
---
Binary under test
~/Library/Application Support/Claude/claude-code-vm/2.1.111/claudefile:ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, for GNU/Linux 3.7.0, BuildID[sha1]=7184009e379dd1270157ebc863f106f3bdd1a40e, not stripped99376866bf7ec367142d3be548c17184a79f30a97318441ee9a00f78e51246e7VERSION:"2.1.111", ... PACKAGE_URL:"@anthropic-ai/claude-code""2.1.111"is baked directly intoapp.asar)Strings evidence on 2.1.111 (identical pattern to 2.1.113)
The full OTLP protobuf schema (
opentelemetry.proto.{trace,metrics,logs,common,resource}.v1.*) is also compiled in, and the dynamic-require code fragment matching your root-cause hypothesis is observable in the binary text:Mc7()resolves tonull→ destructure throws → surroundingtry/catchswallows → zero requests fire. Exactly your root-cause hypothesis.Cowork VM runtime confirmation (new signal)
Ran the console-exporter test inside an active Cowork session (host: Claude Desktop 1.3883.0, macOS Apple Silicon; VM env:
CLAUDE_CODE_IS_COWORK=1,CLAUDE_CODE_ENTRYPOINT=local-agent):Emitted a full
claude_code.session.countdata point to stdout within ~89 ms with full attributes (user.idhashed,user.email,session.idUUID,terminal.type=non-interactive). So the OTel SDK initializes fine inside the Cowork VM — the failure is purely at the OTLP exporter boundary, matching the 2.1.113 behavior.Also verified that the Admin UI → Cowork → Monitoring configuration (
OTEL_EXPORTER_OTLP_ENDPOINT,OTEL_EXPORTER_OTLP_HEADERS,OTEL_LOG_USER_PROMPTS=1, protocol, etc.) does propagate into the VMclaudesubprocess environment. That rules out env-var propagation bugs and narrows this issue exclusively to the exporter packaging.Impact this adds to #50567
Admin → Cowork → Monitoringas a first-class feature and sets expectations that it works; in practice every OTLP backend silently receives zero events. This is the root cause of #39471, where the same reproduction is reported independently against Datadog, ADOT/X-Ray, Azure Application Insights, Sumo Logic, and custom collectors."2.1.111") is baked directly into the Claude Desktopapp.asarmanifest and installed into~/Library/Application Support/Claude/claude-code-vm/<version>/claudeat session start. It does not track the npm CLI release channel. Any fix needs to land in a Desktop release that bumps the pinned VM SDK to a version with the OTLP exporter packages actually bundled.OTEL_LOG_USER_PROMPTS=1. The logs signal (not just metrics) matters as much as metrics here, andOTLPLogExporterhas the same destructure error baked in. A fix needs to cover all three signals.Minor clarification for the bundle check
PrometheusExporteris bundled in the 2.1.111 binary (appeared in strings alongsideConsoleMetricExporterandInMemoryMetricExporter). For standalone-CLI users,OTEL_METRICS_EXPORTER=prometheus+ a local scrape + a Prometheus-remote-write-to-OTLP bridge is a viable workaround today. Not usable for Cowork since there is no way to reach an in-VM Prometheus endpoint from the host through the egress proxy.+1 to the suggested fix
Bundle
@opentelemetry/exporter-{metrics,logs,traces}-otlp-{proto,http}and convert the silent swallow into a loud startup error ("OTLP <signal> exporter configured but package not available — telemetry will not be exported"). Silent no-op on a monitoring configuration is the worst possible failure mode.Suggested labels to add
area:cowork(this manifests as a Cowork blocker in addition to the standalone CLI)regression(confirmed present on at least 2.1.111 through 2.1.113)+1, confirming this Cowork blocker persists on Claude Desktop
1.4758.0 (build fb266c, 2026-04-24), macOS Apple Silicon. Same
behavior as ten-jampa documented on 1.3883.0: settings persist
correctly, env vars propagate (verified via OTEL_METRICS_EXPORTER=
console-style alternative testing), but OTLP exporter never sends.
This issue blocks Cowork OTel observability for our org regardless
of which OTLP backend or endpoint we point at.
i lost two afternoons last week to this exact silent failure. had OTEL_METRICS_EXPORTER=otlp set, collector logs were empty, no errors anywhere, kept second-guessing my collector config before stracing the bun binary and seeing the require() for exporter-metrics-otlp-http getting swallowed in a try/catch. switched to OTEL_METRICS_EXPORTER=console just to confirm metrics were actually accumulating, they were. ended up giving up on otel for usage tracking and pulling the rolling 5-hour and weekly numbers straight off the claude.ai settings/usage endpoint instead, which is what anthropic actually enforces against. the silent fallback is the worst part of this bug, a thrown error on missing exporter would've saved me half a day.
same problem in
2.1.123versionI am not able to reproduce this locally. I can not deduce from the report alone what is going on. Can you please share the specific error you are getting: run again with
--debug-file /tmp/otel.logand share the lines matchinggrep -i "3P telemetry" /tmp/otel.log. this should give me the exact error where it is failing.Adding org-wide evidence from a different Datadog tenant that confirms this regression and helps narrow the package-bundling hypothesis.
Setup
https://otlp.datadoghq.eu/v1/metrics)http/protobuf, no mTLSLast 24h
claude_code.session.countgrouped byversionresource attribute| version | sessions emitted (24h) |
|----------|-----------------------:|
| 2.1.118 | 2 |
| 2.1.119 | 0 |
| 2.1.120 | 0 |
| 2.1.121 | 0 |
| 2.1.122 | 0 |
| 2.1.123 | 0 |
| 2.1.126 | 0 |
So in our tenant, plain HTTP/protobuf (no mTLS) does still work on 2.1.118 but is silent on every 2.1.119+ build observed (including 2.1.126). #52353 reports plain-config also broken on 2.1.118 with mTLS — our data is the dual: 2.1.118 still works for the non-mTLS code path. That's consistent with a packaging/bundling issue where some OTLP exporter modules survive the bundling change and others don't, and the breakage widens further at 2.1.119.
Debug log on 2.1.126 (anonymized)
claude --debugproduces these[3P telemetry]lines:No
getOtlpMetricExportersline, noCreated N metric exporter(s)line — i.e. the metric-exporter init branch never executes (or throws inside the silent try/catch and is swallowed). This matches the bundled-package hypothesis in the OP: the metric OTLP exporter dynamic require fails, the catch swallows, and that branch silently no-ops.Other diagnostics
DD-API-KEYvalidated independently (curl … /v1/metrics --data-binary ''→400 Payload is empty) — credentials and endpoint healthy.printenv | grep OTEL), so this isn't an env-var availability issue.Asks
WARN-level[3P telemetry]line (not silently swallowed intry/catch). Right now this is debuggable only by side channels (network captures or watching backend ingestion) which is brutal at fleet scale.(Filed our duplicate as #56021; closing that and pointing here.)
Hi — any update on this? Still reproducing on v2.1.143.
Could someone from Anthropic confirm whether a fix is being tracked? Thanks!
@JuanNephrota @mhegazy @andersenleo @ten-jampa @AZVGBND
Update from our side:
Confirmed working on v2.1.143 with mTLS after switching
from OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE / _CLIENT_KEY
to CLAUDE_CODE_CLIENT_CERT / CLAUDE_CODE_CLIENT_KEY
(the correct vars for http/protobuf per the updated mTLS docs).
Still not working for us - sending OTEL to Datadog :/
Ping! Anyone with an update, or successfully working metrics in Claude Code?
Alright - I can verify metrics works for us now. And might have been working all along.
We accidentally used a stale Datadog API Key 🙈. So - this works for us now.
Can any one confirm this actually is working, we are running into the same issue on the latest version of Claude Code, console export Works, so we know that is valid, Manual calls using claude work, so we know thats not the issue, but automatic OTLP exports do Nothing