[Bug] OTel: skill_activated masks skill.name to "custom_skill" while api_request events and cost/token metrics export it verbatim (regression since ~2.1.126)

Open 💬 1 comment Opened Jul 12, 2026 by matthewalton

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet (closest prior report, #58674, was closed by the stale bot without resolution — details below)
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

With telemetry enabled and default privacy settings (OTEL_LOG_TOOL_DETAILS unset), the skill.name attribute is masked on claude_code.skill_activated events for user-defined skills — but the same export, in the same default configuration, carries the exact skill name verbatim on claude_code.api_request events and on the cost.usage/token.usage metrics.

Verified on v2.1.207 by capturing raw OTLP payloads (protoc --decode_raw) from a skill committed to project settings. All three records are from one session:

claude_code.skill_activated event — masked:

1: "skill.name"        → "custom_skill"
1: "invocation_trigger" → "user-slash"
1: "skill.source"      → "projectSettings"

claude_code.api_request event, same session, same default settings — verbatim:

1: "skill.name" → "wield-mask-probe"

claude_code.cost.usage / claude_code.token.usage metrics — verbatim:

1: "skill.name" → "wield-mask-probe"

This makes the masking the worst of both worlds:

  1. It provides no privacy. Anyone with access to the telemetry backend already sees the real skill name on api_request events and the metrics. Masking one event type hides nothing.
  2. It breaks exactly the event designed for skill analytics. skill_activated is the only signal carrying invocation_trigger and skill.source, and it's the one place the name is redacted — so per-skill adoption analysis (which skills does the team actually invoke, and how) can't use it.

Also verified: skill.source=projectSettings skills are masked identically to userSettings ones. The monitoring docs don't say whether repo-committed project skills were meant to be treated differently.

The only unmask is OTEL_LOG_TOOL_DETAILS=1 (verified working on v2.1.207), but that is documented to also export tool parameters — a much bigger privacy surface than skill names. For teams doing direct OTLP export (no collector to strip attributes), that trade is not acceptable just to recover a name that the rest of the export already includes.

History — this appears to be a regression

#58674 reported this same behaviour and included payload evidence that skill_activated carried verbatim skill.name (plus plugin.name/marketplace.name) up to v2.1.112, and became custom_skill somewhere around the v2.1.126 rewrite ("skill_activated now fires for user-typed slash commands"). That issue was closed by the stale bot as NOT_PLANNED without a decision. Related: #50631 (skillOverrides ignored for project settings), #55187 (monitoring docs gaps around skill_activated).

What Should Happen?

Either of these would resolve it; the first matches pre-2.1.126 behaviour:

  1. Restore verbatim skill.name on skill_activated for user-defined skills — consistent with api_request events and the metrics, which already export it at default settings. If the masking is intentional, it should at least be consistent across signals, and the intent documented.
  2. If a control is preferred, make it scoped rather than all-or-nothing — e.g. a managed-settings option to opt user-defined (or specifically repo-committed projectSettings) skills into verbatim event reporting without coupling it to OTEL_LOG_TOOL_DETAILS's tool-parameter export. An org that deploys telemetry via managed settings owns the skills committed to its own repos; their names are not sensitive to that org's own backend.

Steps to Reproduce

  1. Point telemetry at a local OTLP listener via project settings env:
{
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4318"
  }
}
  1. Add any skill under the project's .claude/skills/<name>/SKILL.md.
  2. Run claude -p "/<name>" and capture the OTLP payloads.
  3. Compare skill.name on the skill_activated event (custom_skill) vs the api_request event and cost.usage/token.usage metrics (verbatim skill name).

Environment

  • Claude Code v2.1.207
  • macOS (darwin 25.5.0), arm64
  • Export: OTLP http/protobuf, direct (no collector); OTEL_LOG_TOOL_DETAILS and OTEL_LOG_USER_PROMPTS unset

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗