Skip to content

Bug: Claude Code's second User-Agent is unrecognised, so ~13% of its events label as Other #1363

Closed

Description

@Alan-Cha

What happens

Claude Code sends three distinct User-Agents on live traffic, and knownClients recognises only one. Measured from one laptop proxy's session store:

User-Agent Events ParseUserAgent().Name
claude-cli/2.1.233 (external, claude-vscode, agent-sdk/0.3.233) 469 claude-code ✅
claude-code/2.1.233 (claude-vscode, agent-sdk/0.3.233) 70 "" ❌
Claude-User (claude-code/2.1.233; +https://support.anthropic.com/) 12 "" — deliberate, see below

The middle row is the bug: claude-code is a value in knownClients but never a key. ParseUserAgent looks the first product token up against the map's keys, and that token is claude-code, so the lookup misses and the traffic is unrecognised.

Why it matters

Those 70 events — ~13% of this capture's Claude Code traffic — pool into agentop's catch-all Other row in the AGENTS pane instead of claude-code, and their spend goes with them. pipeline.IsKnownAgent is false for them, so they are also excluded wherever that gates (core/cost/usage). Claude Code is the reference integration, so this is the one agent whose attribution was assumed correct.

Session attribution is unaffected — AffinityName() returns claude-code for all three, because its comment-scan fallback compares against canonical names and claude-code is one. So this is a labeling/aggregation bug, not a session-grouping one.

The third row is not a bug

Claude-User (claude-code/…) staying unrecognised is intentional, and AffinityName's doc comment explains why: reading an agent's name out of a comment into Name would move those rows to a different agent in the ledger and the AGENTS pane than the history beside them. Leave it as is.

Suggested fix

One entry:

"claude-cli":  "claude-code",
"claude-code": "claude-code",   // the Agent-SDK-shaped UA

I probed this locally and verified three things:

  1. claude-code/2.1.233 (…) labels as claude-code / version 2.1.233.
  2. Claude-User (claude-code/…) still does NOT label — its first token is Claude-User, and trailingKnownClient cuts on / without trimming punctuation, so the later token reads as (claude-code and misses. The deliberate behaviour survives.
  3. The existing core/pipeline suite stays green.

Worth adding a TestParseUserAgent_* row for each of the three live UAs above, including the negative for Claude-User, so the deliberate case is pinned alongside the fixed one.

How this was found

While verifying Codex's agent recognition for #1337, which fixed the same class of bug for Codex: an agent that sends more than one product token, with the lookup table covering only some of them. Codex needed codex-tui and codex_cli_rs added beside codex_exec for exactly this reason. Worth a look at whether bob/bob-shell/opencode have unlisted second tokens too — OpenCode reportedly uses one UA for every mode, but that is from a source comment rather than a capture.

Not fixed in #1337 deliberately: that PR is Codex support, and this is a different agent.

Unknown

Whether headless Claude Code (claude -p) is one of these two shapes or a fourth. Both recognised shapes mention claude-vscode and agent-sdk, so they look like different components (CLI binary vs Agent SDK) rather than interactive-vs-headless, but I have no headless capture to confirm it.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

Activity

  1. self-assigned this
    on Oct 13, 2026
  2. added a commit that references this issue on Oct 14, 2026
    c8881de
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions