[BUG] otelHeadersHelper did not return a valid value on Windows — helper binary never invoked at time of error
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?
The PeriodicExportingMetricReader reports otelHeadersHelper did not return a valid value on Windows every 60 seconds, even though the configured helper binary returns valid JSON every time it is actually called.
We instrumented our helper with forensic diagnostic logging that captures the exact bytes written to stdout using a write-before-and-after pattern (writes a "started" entry before any work, then a "completed" entry with the raw stdout_json after writing output). This proves:
| Time (UTC) | Event | Detail |
| ---------- | ----------------- | -------------------------------------------------------------------------------- |
| 18:16:22 | Helper called | started + completed entries, valid JSON captured in stdout_json |
| 18:17:11 | Claude Code error | otelHeadersHelper did not return a valid value — no started entry exists |
The helper binary was never invoked at the time of the error. Our diagnostic log has no started entry at 18:17:11, meaning Claude Code's process never spawned.
The helper output (captured byte-for-byte): clean JSON object with string key-value pairs, no BOM, no \r, no stderr bleed, \n terminator, completes in 5–35ms.
What Should Happen?
When otelHeadersHelper is configured and the helper binary returns valid JSON (object with string key-value pairs), the PeriodicExportingMetricReader should successfully use those headers for metric export. The error should not appear.
Error Messages/Logs
2026-03-27T18:17:11.356Z [ERROR] [3P telemetry] OTEL diag error: {
"message": "PeriodicExportingMetricReader: metrics export failed (error Error: otelHeadersHelper did not return a valid value)",
"sourceURL": "B:/~BUN/root/src/entrypoints/cli.js",
"line": "2717",
"column": "307976",
"stack": "Error: PeriodicExportingMetricReader: metrics export failed (error Error: otelHeadersHelper did not return a valid value)\n at _doRun (B:/~BUN/root/src/entrypoints/cli.js:2717:307976)\n at processTicksAndRejections (native:7:39)"
}
Steps to Reproduce
- On Windows, configure
otelHeadersHelperin~/.claude/settings.jsonpointing to a Go binary that outputs valid JSON headers:
``json``
{
"otelHeadersHelper": "C:/path/to/credential-provider.exe otel-helper --profile my-profile",
"env": {
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://otel-collector.example.com",
"OTEL_METRICS_EXPORTER": "otlp"
}
}
- Start Claude Code in VS Code
- Open the "Anthropic.claude-code.Claude VSCode" output channel
- Within 60 seconds, observe the
PeriodicExportingMetricReader: metrics export failederror - The error repeats every 60 seconds indefinitely
Note: The helper binary completes in 5–35ms. The bug may be related to Bun's spawnSync not capturing stdout from fast-completing processes on Windows.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.84 (VS Code extension, anthropic.claude-code-2.1.84-win32-x64)
Platform
AWS Bedrock
Operating System
Windows
Terminal/Shell
Windows Terminal
Additional Information
Suspected root cause: The helper's 29-minute debounce cache (CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS, default 1,740,000ms) means PeriodicExportingMetricReader (60s interval) serves from cache. From decompiling the extension binary, the code path is:
let result = spawnSync(command, { timeout: 30000, shell: true })
?.toString()
.trim();
if (!result) throw Error("otelHeadersHelper did not return a valid value");
// on success: cache = parsed; lastCall = Date.now();
If the first invocation after process start fails to capture stdout (Bun's spawnSync with shell: true on Windows for fast-completing processes), the empty/null result is never cached (the function throws before caching), so every subsequent 60-second metric tick re-throws the same error without re-invoking the helper — because the function throws before reaching the cache-set line.
This is distinct from #35207 (gRPC protocol issue) — we use http/protobuf.
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗