[Bug] Agent tool registry fails to discover subset of valid .claude/agents/*.md files
Bug Description
Agent tool's subagent_type registry only exposes a subset of custom agents defined in .claude/agents/*.md.
Setup: project has 15 agent definition files in .claude/agents/ (all valid, same format, same directory). Only 8 of them show up as dispatchable subagent_types via the Agent tool: gray, holt, nolan, pax, quill, reeve, sable, wren.
Missing (files exist on disk, never appear as a subagent_type): auden, iris, marlow, prue, reid, sloane, vance.
Notable: after the gap was first noticed (7 of 14 agents missing at the time), a new agent (holt) was created mid-session and registered successfully without any restart. So new hires can register live — it's specifically this fixed set of 7 pre-existing agents that never registers, not a general "mid-session additions don't load" issue.
Tried: fully quit all Claude Code processes/windows (confirmed via Get-Process claude returning nothing), relaunched, and used claude --resume to reopen the same session. Same 7 agents still missing after the full cold restart — identical missing set before and after. Also reproduced identically in both the VS Code integrated terminal and a standalone Windows Terminal / PowerShell session — same 7 agents missing in both, ruling out a terminal-specific or IDE-integration cause. Rules out simple session-cache/snapshot-lag as the explanation, since both a live mid-session add (holt) worked and a full cold restart didn't fix the other 7.
Environment: Claude Code CLI 2.1.220, Windows 11 Pro (build 10.0.26200), win32 platform, tested in both windows-terminal and VS Code integrated terminal. Related feedback ID from in-app /bug report: c11d04a8-92c1-4b7f-868d-4b94eebce23f.
Expected: all 15 files in .claude/agents/ should register as dispatchable subagent_types.
Actual: only 8 do, consistently, across restarts, across terminals, and across a live mid-session addition.
3 Comments
Thanks for the detailed report. I tried to reproduce on the released 2.1.233 (macOS) with a project containing 15 files in
.claude/agents/, all valid, and all 15 showed up as subagent types. So a fixed subset going missing points at something specific to those 7 files rather than a general cap on discovery.Three things I did reproduce that make a file silently disappear from the agent list on 2.1.233 (no error shown, and the file otherwise looks identical):
Out-File/Set-Content -Encoding UTF8and some editors write a BOM by default. The frontmatter is then not recognized and the agent is skipped with no message at all — that's a bug on our side (it should at least be reported), and we'll look at making BOM'd files load.name:in their frontmatter (e.g. a copied template) — only one loads.claude --debugoutput as "failed to parse").Could you check the 7 missing files: run
Format-Hex .claude\agents\auden.md -Count 8(a leadingEF BB BForFF FEis the BOM/UTF-16 case), confirm eachname:value is unique across all 15 files, and runclaude --debugand look for lines mentioning "Duplicate agent name" or "frontmatter ... failed to parse"? If none of those apply, please paste one of the missing files verbatim (frontmatter included) so we can dig further.🤖 Generated with Claude Code
We weren't able to reproduce this. Could you provide steps to trigger the issue — what you ran, what happened, and what you expected? This issue will be closed automatically if there's no activity within 7 days.
Thanks — your third cause was the right one, and running
--debugas you suggested is what cracked it. I have a root cause and a minimal reproduction that does not require Windows.Summary: this is not agent discovery. It is YAML frontmatter parsing, and the behaviour is line-ending dependent. An unquoted
description:plain scalar containing:(colon followed by a space) parses fine when the frontmatter block uses LF, and fails to parse when the same frontmatter block uses CRLF. The affected file is then dropped from the agent registry with no message anywhere outside--debug. That asymmetry is why it does not reproduce on macOS.---
Your three checks
| # | Check | Result |
|---|---|---|
| 1 | UTF-8 BOM / UTF-16 | Ruled out. All 18 files begin
2d 2d 2d 0d 0aor2d 2d 2d 0a(---). No BOM, no UTF-16 — on the registered files and the missing files alike. || 2 | Duplicate
name:values | Ruled out. All 18name:values are unique, and every one matches its own filename stem. NoDuplicate agent nameline appears in--debug. || 3 | Broken YAML frontmatter | Confirmed — this is the cause.
--debugemits exactly 10 warnings, naming exactly the 10 agents that fail to register. |claude --debugin the real project, filtered:Ten warnings, ten missing agents, exact set match. Apologies for filing this as a discovery/registry bug — the warning was there the whole time and I had never run
--debug.That said, I think there is still a real defect here, described in "What I think the actual bug is" below.
---
Isolating the trigger
I built seven agent files that are identical except for one controlled property each, and ran a fresh
claude -p ... --debug-fileagainst them.| Agent file | Frontmatter line endings | Body line endings |
description:contains | Registers? ||---|---|---|---|---|
|
alphaclean.md| CRLF | CRLF | no:| yes ||
bravocolon.md| CRLF | CRLF | unquoted:| no ||
charlielf.md| LF | LF | unquoted:| yes ||
deltaquoted.md| CRLF | CRLF |:inside a double-quoted scalar | yes ||
echonospace.md| CRLF | CRLF |:with no following space | yes ||
foxtrotlffm.md| LF | CRLF | unquoted:| yes ||
golfcrlffm.md| CRLF | LF | unquoted:| no |Only
bravocolon.mdandgolfcrlffm.mdproduced aFailed to parse YAML frontmatterwarning, and only those two were absent from the agent list.So the trigger is precisely:
Three independent things each fix it: quote the scalar, remove the colon-space, or switch the frontmatter to LF. Body line endings are irrelevant —
foxtrotlffm(LF frontmatter, CRLF body) registers andgolfcrlffm(CRLF frontmatter, LF body) does not.---
Why this did not reproduce for you
description: some text: more textis, strictly speaking, invalid YAML regardless of line endings — a plain scalar cannot contain:. Both PyYAML and a standards-conformant JS parser reject it either way.But Claude Code only rejects it under CRLF. Under LF it accepts the same logical content. My working hypothesis — and I have not read your source, so treat this as a pointer rather than a claim — is that there is a fast-path extraction for the common
key: valuefrontmatter shape that succeeds under LF and never invokes the real YAML parser, and that a trailing\ron each line causes that fast path to miss and fall through to the strict parser, which then correctly rejects the colon-space. Whatever the mechanism, the observable behaviour is a hard platform split.The practical consequence is the part I would flag: the same git commit produces a different set of registered agents on Windows than on macOS/Linux. Any repo cloned with
core.autocrlf=true(the Windows default), or any agent file written by a Windows editor, gets CRLF frontmatter and starts silently losing agents whose descriptions contain ordinary prose punctuation. On macOS the identical file works. That makes it invisible to a maintainer testing on macOS, and invisible in code review, since the offending byte is\r.In my case one file,
miles.md, happens to have been written with LF-only line endings while the other 17 are CRLF — andmiles.mdregisters correctly despite having a colon-space in its description. It was the sole outlier, and it is what pointed at line endings in the first place.---
What I think the actual bug is
Two things, both small:
description: a: bis your call — but it should give the same answer on Windows and macOS. Right now identical content behaves differently, and the difference is an invisible character.Agenttool'ssubagent_typelist or in/agents. The[WARN]only exists under--debug. Since the warning is already being generated, surfacing it once at startup — or listing the file in/agentsas failed-to-load with its parse error — would turn a multi-week mystery into a five-second fix. That is the change I would most like to see.A validation lint (
claude agents check, or a warning when/agentsis opened) would cover it even more cleanly.---
Minimal reproduction — macOS/Linux, no Windows required
The
\ris what matters, soprintfis used to make the line endings explicit:Expected (buggy) result:
alphacleanandcharlielfboth appear in the listedsubagent_typevalues;bravocolondoes not.grepreturns exactly one line, namingbravocolon.md.B and C differ only in line endings. That difference alone decides whether the agent exists.
---
Environment
.claude/agents/: 18gray,miles,nolan,pax,quill,reeve,sable,wrenauden,holt,iris,marlow,prue,reid,sloane,vance,vera,wardname:values unique and matching their filenames; no BOM on any file; 17 of 18 files are CRLF throughout,miles.mdis LF-only.---
Correction to my original report
My original report speculated that newly created agent files register correctly while already-broken ones stay broken. That is wrong and I withdraw it. Two agents created after filing (
vera,ward) registered at first and are now in the failing set, and one (holt) was working in July and later stopped. In every case the actual change was an edit that introduced a colon-space into thedescription:field — nothing to do with when the file was created. There is no time-based or ordering component to this at all.I also want to record a hypothesis I tested and disproved, in case it saves anyone else the detour: file size and description length are not the discriminator, despite correlating suspiciously well. The largest registered file is 16,234 bytes and the smallest failing one is 10,340 bytes; the longest registered
description:is 1,595 characters and the shortest failing one is 1,228. Both distributions overlap and neither is separable. Long descriptions simply have more opportunities to contain a colon.---
Smallest failing file, verbatim
.claude/agents/reid.md— the frontmatter block, byte-for-byte (the file's remaining ~10 KB is ordinary Markdown prose below the closing---and plays no part in the parse; every line below ends CRLF):The offending sequence is at character 197 of the description:
per Thilo's direct decision): equal-standing— a colon followed by a space, in ordinary English prose, in an unquoted scalar. Under LF this file loads. Under CRLF it does not.For completeness, the first colon-space in each of the other nine failing files is the same shape — normal prose punctuation, e.g.
governed config: classify,business intelligence: owns,brought to Thilo: verifies. Nothing exotic, and nothing a reasonable author would expect to be a syntax error.---
Happy to test a patch build, run any additional variant you want, or supply the full
--debuglog. Please reopen if useful — the platform-dependent parse and the silent drop both seem worth fixing independently of my own malformed files.