OTEL: claude_code.plugin_loaded dropped on every session start — event logger not initialized yet (same pattern as #58439)

Status Closed — not planned
Reported on v2.1.219
Maintainer reply None cached
Activity 3 comments · opened Jul 24, 2026 · closed Aug 10, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet for this specific event (related: #58439, same underlying pattern for a different event)
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code available via npm at time of testing (confirmed changelog through 2.1.219 contains no related fix)

What's Wrong?

Summary

claude_code.plugin_loaded is dropped on every session start, for every installed plugin, because it fires before the OTel event logger finishes initializing. This is the same underlying race reported in #58439 for hook_execution_start (closed stale, unfixed) — a different event, identical root cause and log signature. Filing separately since #58439 is closed/stale and this instance is on the native CLI (not the VS Code extension) and affects plugin-load attribution specifically, but the fix is almost certainly shared.

Environment

  • Claude Code: 2.1.206 (native CLI, macOS)
  • Deployment: enterprise/MDM-managed (/Library/Application Support/ClaudeCode/managed-settings.json present, sets CLAUDE_CODE_ENABLE_TELEMETRY=1, OTEL_LOGS_EXPORTER=otlp, OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io, and related OTLP env vars)
  • OS: macOS darwin 25.5.0 arm64
  • 2 plugins installed via a git-subdir marketplace source (one with a bare SKILL.md manifest under strict: false inline mode, one — tested locally — with a real .claude-plugin/plugin.json); both exhibit the identical drop, confirming the manifest shape is irrelevant to this bug

Reproduction

rm -f /tmp/claude-debug.log
claude --debug-file /tmp/claude-debug.log --print "hi" >/dev/null 2>&1
grep -i "3P telemetry\|event logger" /tmp/claude-debug.log

Output (reproduced identically across 3 consecutive clean runs):

2026-07-24T19:18:43.746Z [DEBUG] [3P telemetry] Waiting for remote managed settings before telemetry init
2026-07-24T19:18:43.750Z [DEBUG] [3P telemetry] Remote managed settings loaded, initializing telemetry
2026-07-24T19:18:43.776Z [DEBUG] [3P telemetry] isTelemetryEnabled=true (CLAUDE_CODE_ENABLE_TELEMETRY=1)
2026-07-24T19:18:43.776Z [DEBUG] [3P telemetry] getOtlpReaders: types=[], interval=60000, protocol=undefined, endpoint=undefined
2026-07-24T19:18:43.776Z [DEBUG] [3P telemetry] getOtlpLogExporters: types=[], protocol=undefined, endpoint=undefined
2026-07-24T19:18:43.777Z [DEBUG] [3P telemetry] Created 0 log exporter(s)
2026-07-24T19:18:43.785Z [WARN] [3P telemetry] Event dropped (no event logger initialized): plugin_loaded

Cross-checked against the org's live Honeycomb backend (claude-code dataset — note: a separate dataset from the more commonly-referenced claude dataset, which does not carry this event at all): a query for plugin.name exists AND marketplace.name = <our marketplace> over the trailing 7 days returns zero results, consistent with the event being dropped client-side on every session rather than merely mis-attributed or delayed in export.

Root cause

  • Telemetry init is gated on an async network fetch ("Waiting for remote managed settings before telemetry init") before the event logger/exporters are constructed.
  • Plugin loading is synchronous local disk I/O (reading installed_plugins.json + each plugin's cached directory) and fires plugin_loaded immediately per plugin, with no wait on telemetry readiness.
  • Local disk I/O reliably finishes before a network round-trip completes, so plugin_loaded always loses this race — this is not an intermittent race, it reproduced 3/3 in testing.
  • This is identical in shape to #58439's hook_execution_start drop (~37ms gap there vs. plugin loading here), suggesting a general pattern: any event fired during CLI startup, before telemetry init resolves its network-dependent managed-settings fetch, is silently and permanently dropped.

What Should Happen?

plugin_loaded (and other startup-window events, per the #58439 pattern) should reach the configured exporter(s) rather than being silently dropped. Suggested fixes, in order of preference:

  1. Buffer + flush: queue events fired before the event logger exists, replay them once telemetry init completes. Lowest risk, doesn't change startup ordering or latency.
  2. Defer emission: hold plugin-load's plugin_loaded call until telemetry init resolves (only safe if this doesn't materially delay plugin availability).
  3. Local-first telemetry init: don't gate event-logger construction on the remote managed-settings network fetch; initialize with current env/local settings synchronously, merge remote managed settings in asynchronously afterward.

A minimal stop-gap, if a full fix isn't feasible short-term: emit a dropped-event counter metric so consumers at least know events are being lost, rather than silent, unrecoverable data loss (also suggested in #58439).

Error Messages/Logs

See reproduction section above — [WARN] [3P telemetry] Event dropped (no event logger initialized): plugin_loaded, reproduced 3/3 via --debug-file.

Related

  • #58439 — same root-cause pattern (hook_execution_start), closed stale without a fix; this issue reproduces the identical pattern for a different event on native CLI (not VS Code extension) and confirms it's unrelated to plugin manifest shape (plugin.json vs. inline SKILL.md + strict: false)
  • #67657 — a related but distinct telemetry-init-ordering bug (managed-settings env block arriving too late for the OTEL SDK)

View original on GitHub ↗

3 Comments

amber-beasley-liatrio · 2 months ago

hit the same underlying attribution gap independently and can add some corroborating (and slightly complicating) data from live org telemetry.

Partial contradiction of the "100% drop" claim: in our org's Honeycomb claude-code dataset, a third-party plugin (unrelated to us, confirmed via its own SKILL.md) shows 39 successful plugin_loaded events over a trailing 7-day window, each with correct plugin.name/marketplace.name attribution. So the drop isn't universal/deterministic in our environment the way the 3/3 local repro suggests — it's evidently intermittent or session-dependent at scale, which is still consistent with your race-condition theory (a network-gated telemetry init racing local plugin load), just not a 100%-reproducing one in every deployment.

A possibly-separate, starker pattern: our own org's plugin marketplace (a git-subdir source where every plugin relies on a bare SKILL.md + strict: false inline-manifest mode, no real .claude-plugin/plugin.json) shows zero plugin_loaded events, ever, across many sessions and multiple distinct plugins, over the same window the third-party plugin above got 39 through. If this were purely the startup race you describe, we'd expect at least some of our sessions to win the race the same way that other plugin's sessions sometimes do. A 0-for-many pattern next to a 39-for-many pattern (different plugin, same client version, same managed-settings deployment) makes us suspect there may be a second, compounding cause specific to plugins using the inline-SKILL.md/strict: false manifest path — separate from, but possibly interacting with, the startup-init race you've diagnosed here. Not certain this is the case, but wanted to flag it in case it's useful for narrowing the fix, since a buffer-and-flush fix for the race condition might not fully resolve our side's zero-ever pattern if a second cause is also in play.

Happy to pull more detail (exact claude --version, debug logs) if useful for reproducing the inline-manifest variant specifically.

amber-beasley-liatrio · 2 months ago

Following up with a live verification against the current CLI.

Re-ran this issue's exact reproduction on 2.1.226 (claude --debug-file ... --print "hi", grep for telemetry/event-logger/dropped lines), no local env overrides, same org-managed managed-settings.json rollout (OTLP → Honeycomb) described in this issue. 3/3 clean runs:

[3P telemetry] getOtlpReaders: types=["otlp"], interval=60000, protocol=http/protobuf, endpoint=https://api.honeycomb.io
[3P telemetry] Created 1 log exporter(s)
[3P telemetry] Event logger set successfully

No types=[] / Created 0 log exporter(s) / Event dropped in any run — a different telemetry-init outcome than what this issue documented on 2.1.206.

Cross-checked in our Honeycomb backend: for the matching session, event.name = plugin_loaded landed correctly for all 4 of my currently-installed plugins, each carrying correct marketplace.name/plugin.name attribution, timestamped ~5ms after "Event logger set successfully" — no drop.

Likely explanation: the types=[] / Created 0 log exporter(s) sequence in this issue's own reproduction matches a separate, already-known startup bug we tracked independently — telemetry init could complete with zero OTLP exporters despite CLAUDE_CODE_ENABLE_TELEMETRY=1 reading correctly (observed on 2.1.212 in our fleet, no longer reproducing by 2.1.223+ in our testing). This issue was filed against 2.1.206, before that fix landed. It's likely what got attributed here to a plugin_loaded-specific startup race was actually that broader (since-fixed) zero-exporter bug, rather than a distinct ordering issue specific to plugin loading.

Given the negative repro on current (2.1.226), closing this out. The underlying "events fired before the logger initializes get dropped, no buffer/flush" code path does still exist (visible in the current build as Event dropped (no event logger initialized): ${eventName}), so a race under different startup conditions isn't categorically ruled out — just not observed here across repeated testing. Happy to reopen with a fresh repro on a current version if anyone hits it.

github-actions[bot] · 16 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.