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 2 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 ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗