3P OpenTelemetry telemetry not initializing on enterprise/managed accounts (Windows)

Status Open
Reported on v2.1.98
Maintainer reply None cached
Activity 5 comments · opened Apr 10, 2026

Summary

OpenTelemetry 3rd-party telemetry export does not initialize on enterprise/managed accounts when running Claude Code on Windows. Environment variables are correctly set and visible in the Claude Code Node.js process, but no OTLP connections are established and no data is exported — not even with console exporters.

Environment

  • Claude Code version: 2.1.98 (@anthropic-ai/claude-code)
  • Platform: Windows 11 Pro 10.0.22631
  • Node.js: v25.3.0
  • Shell: Git Bash (via PowerShell terminal)
  • Account type: Enterprise/managed (OAuth authentication)
  • Docker: Rancher Desktop (containers running on WSL2)

Configuration

Exact configuration as documented at https://code.claude.com/docs/en/monitoring-usage:

export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="otlp"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"

Also tested with:

  • http/json protocol on port 4318
  • http/protobuf protocol on port 4318
  • console exporters (no output produced)
  • OTEL_METRICS_EXPORTER=none (logs only)
  • Various combinations of the above

Verified conditions

1. Environment variables ARE set in the Claude Code process

Confirmed via PowerShell GetProcessById().StartInfo.EnvironmentVariables:

CLAUDE_CODE_ENABLE_TELEMETRY = 1
OTEL_METRICS_EXPORTER = otlp
OTEL_LOGS_EXPORTER = otlp
OTEL_EXPORTER_OTLP_PROTOCOL = grpc
OTEL_EXPORTER_OTLP_ENDPOINT = http://localhost:4317

2. OTel Collector is running and reachable

All containers healthy (Grafana, Loki, Prometheus, Tempo, OTel Collector). Manual OTLP requests from both curl and Node.js succeed — data arrives in Loki.

3. Claude Code never connects to the collector

Verified via Get-NetTCPConnection — the Claude Code process has zero TCP connections to ports 4317 or 4318. Only connections to Anthropic API servers (port 443).

4. Console exporter produces no output

With OTEL_METRICS_EXPORTER=console and OTEL_LOGS_EXPORTER=console, no JSON telemetry data appears in the terminal. This confirms the telemetry pipeline is never initialized, not a network/connectivity issue.

5. No managed settings file exists

Checked C:\ProgramData\ClaudeCode\managed-settings.json — directory does not exist. No MDM-deployed configuration overriding user settings.

Root cause analysis (from source code investigation)

We traced the initialization flow in the minified cli.js (v2.1.98):

Telemetry init flow

_r8() → checks Eo1() (managed account?) → waits for fu8() → cl() → J$7() → S6Y()

Managed path routing

The account's OAuth token has subscriptionType === null, causing the yF() function to return true. This routes telemetry init through the managed path, which:

  1. Creates a Promise (B56) with a 30-second timeout
  2. Attempts to fetch remote managed settings from ${BASE_API_URL}/api/claude_code/settings
  3. After timeout or settings load, calls cl() (applies config to process.env) then J$7() (initializes exporters)

However, something in this chain fails silently — the .catch() handler only logs internally via an opaque N() function that is not visible to the user:

fu8().then(async () => {
  cl();
  await J$7();
}).catch((q) => {
  N(`[3P telemetry] Telemetry init failed (remote settings path): ${U6(q)}`);
  // ← Error is swallowed here, user never sees it
});

Beta tracing path also blocked

The beta tracing path (h6Y()) requires a feature flag tengu_trace_lantern that is not enabled for this account:

function jJ() {
  if (!(B6(process.env.ENABLE_BETA_TRACING_DETAILED) && Boolean(process.env.BETA_TRACING_ENDPOINT)))
    return false;
  return Q7() || h8("tengu_trace_lantern", false);  // feature flag not set
}

In interactive mode, Q7() returns false (!isInteractive), and the feature flag is not set, so the beta path is never taken.

Expected behavior

With CLAUDE_CODE_ENABLE_TELEMETRY=1 and valid OTEL exporter configuration, Claude Code should export telemetry data to the configured endpoint, regardless of account type (personal, team, enterprise).

Actual behavior

No telemetry data is exported. No connections are made to the OTEL collector. Console exporters produce no output. The telemetry initialization fails silently on the managed/enterprise account path.

Steps to reproduce

  1. Use an enterprise/managed OAuth account (organization with subscriptionType === null)
  2. Set environment variables as documented
  3. Run claude or claude --resume
  4. Observe no OTLP connections and no console exporter output

Suggested improvements

  1. Surface telemetry initialization errors — at minimum via stderr or a debug log file, so users can diagnose failures
  2. Ensure the managed settings path doesn't silently prevent telemetry init — if remote settings fail to load, telemetry should still initialize with user-provided env vars
  3. Add a claude telemetry status diagnostic command to help users verify their telemetry configuration is working

View original on GitHub ↗

5 Comments

leaolucylia · 4 months ago

Update: Works on WSL2, fails on Windows native

Tested Claude Code v2.1.100 on the same machine, same enterprise account, same Docker containers, same env vars:

| Environment | Result |
|---|---|
| Windows native (Git Bash / PowerShell) | ❌ No telemetry — zero OTLP connections, console exporter produces no output |
| WSL2 (Ubuntu 22.04 on same machine) | ✅ Works perfectly — logs, metrics, events all flowing to collector |

WSL2 Loki data confirms full telemetry: user_prompt, api_request events with cost_usd, model, tokens, session_id, user_email, etc.

This narrows the issue to Windows native execution only. The enterprise/managed account path, Docker connectivity, and OTEL SDK are all fine — something in the Windows-specific initialization prevents the telemetry pipeline from being created.

byronwong-dev · 4 months ago

I faced the same issue for macos environment.

Env detail:
MacOS 26.4
Claude code version: 2.1.108 (via npm installation)
nodejs: v25.9.0

Debugging with claude gets me here:

  The stripping happens BEFORE mlz() is called:                                                                                                                                   
                                                                                                                                                                                  
  // Step 1: Strip "console" from env var                                                                                                                                         
  for(let M of ["OTEL_METRICS_EXPORTER",...]) {                                                                                                                                   
    process.env[M] = P.filter(W => W !== "console").join(",")                                                                                                                     
  }
                                                                                                                                                                                  
  // Step 2: THEN call mlz() which reads the already-stripped env var                                                                                                             
  if(isTelemetryEnabled) q.push(...await mlz())                                                                                                                                   
                                                                                                                                                                                  
  mlz() is the function that reads OTEL_METRICS_EXPORTER and creates ConsoleMetricExporter — but by the time it runs, "console" has already been removed from the env var. The    
  ConsoleMetricExporter is never instantiated.                                                                                                                                    
                                                                                                                                                                                  
  This is a bug in Claude Code 2.1.108. The intent was likely to prevent the OTEL SDK from creating a duplicate console exporter, but the stripping accidentally prevents the     
  manual creation too.
                                                                                                                                                                                  
  Practical workarounds:

  1. Your metrics ARE flowing — to https://<redacted> via OTLP. Check whatever dashboard is attached to that endpoint for your actual data.                       
  2. For local console debugging, run a lightweight OTEL collector that prints to stdout:
  # Using otelcol or the contrib distro with debug exporter                                                                                                                       
  OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318/v1/metrics \                                                                                                          
  OTEL_METRIC_EXPORT_INTERVAL=1000 claude -p "hello world"                                                                                                                        
  2. Then point a local collector at port 4318 with debug exporter.                                                                                                               
  3. File a bug at the Claude Code repo — the ConsoleMetricExporter code exists but is unreachable due to the premature stripping
rafalpronko · 4 months ago

Additional repro: macOS personal plan also affected (v2.1.117)

Confirming this bug is not scoped to enterprise/managed accounts — reproduces on a personal plan on macOS.

Environment

  • Claude Code: 2.1.117 (@anthropic-ai/claude-code, latest on npm)
  • - Platform: macOS Darwin 24.6.0 (arm64)
  • - - Account: Personal plan (not managed, not enterprise)
  • - - - Shell: zsh
  • - - - - OTLP target: Grafana Cloud (https://otlp-gateway-prod-eu-north-0.grafana.net/otlp), http/protobuf

Config (all in ~/.zshrc)

export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-gateway-prod-eu-north-0.grafana.net/otlp"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic%20<redacted>"
export OTEL_SERVICE_NAME=claude-code
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=delta
export OTEL_METRIC_EXPORT_INTERVAL=10000
export OTEL_LOGS_EXPORT_INTERVAL=5000

Verified

  1. Env vars reach the Claude Code processps eww -p <pid> confirms all OTEL_* variables are present.
  2. 2. OTLP endpoint + auth work — manual curl -X POST $OTEL_EXPORTER_OTLP_ENDPOINT/v1/logs with the same Authorization header returns HTTP 204, and /v1/metrics returns HTTP 200. So the endpoint, region, and token are valid.
  3. 3. No managed settings/Library/Application Support/ClaudeCode/ and /etc/claude-code/ do not exist. ~/.claude/settings.json contains no telemetry overrides.
  4. 4. Claude CLI process has ZERO TCP socketslsof -p <claude-cli-pid> -iTCP shows only txt/cwd/tty entries owned by the 2.1.117 process; no established or pending connections to grafana.net, otlp-gateway, the resolved Grafana IP, or even api.anthropic.com. The OTEL pipeline is never initialized, same symptom as OP describes on Windows.
  5. 5. Console exporter also silent in interactive mode — matches @byronwong-dev's finding on macOS 26.4 / v2.1.108.

Interesting: non-interactive (claude -p) path works

When running claude -p "..." with OTEL_METRICS_EXPORTER=console OTEL_LOGS_EXPORTER=console, telemetry does emit on stdout (3 metrics: claude_code.cost.usage, claude_code.session.count, claude_code.token.usage; ~46 events including mcp_server_connection, hook_execution_complete, etc.). This matches the root-cause analysis in the original report — Q7()/!isInteractive takes a different code path.

So the bug specifically affects interactive TUI mode and is independent of account type. The silent .catch() in the managed-settings init flow described by OP likely fires even on personal accounts when remote settings fetch / init otherwise fails.

Suggestion

Echoing OP's item #1 — please surface telemetry init errors on stderr or via a claude telemetry status diagnostic. Right now users have no signal that init failed short of running lsof on the process.

Happy to help further debug if helpful.

ezitisitis · 3 months ago

Claude asked to provide a little feedback to this ticket:

Confirming this on macOS 14 (Apple Silicon) with a Claude Max subscription. Tested with versions 2.1.142, 2.1.143, and 2.1.150 via native installer.
Minimal repro:

CLAUDE_CODE_ENABLE_TELEMETRY=1 \
OTEL_METRICS_EXPORTER=console \
OTEL_METRIC_EXPORT_INTERVAL=1000 \
claude -p "hi" 2>&1 | tee /tmp/test.log
grep -c claude_code /tmp/test.log
# → 0 on all three versions

Console exporter produces no metric output. Direct OTLP to a verified collector (TLS, bearer auth, OTLP receiver all confirmed working via curl POST returning 200) also produces nothing. Env vars confirmed present in the process. No error output anywhere — silent init failure.
Suggests the bug is not limited to Windows or enterprise accounts as originally reported.

elladziv · 15 hours ago

Bug also occurs on VSCode on Mac, with Claude extension.