claude-code - 💡(How to fix) Fix tools: frontmatter — misleading error message + silent acceptance of non-documented Agent(a), Agent(b) syntax [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#52293Fetched 2026-04-24 06:10:59
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
labeled ×5

Two related usability issues in the tools: frontmatter parsing of subagent definition files (.claude/agents/*.md). Neither is a parser bug per se — the parser behaves logically — but they compound into a confusing 4-hour debugging session when a user writes a non-documented variant of the Agent(...) syntax:

  1. Error message is misleading. When a subagent invocation is blocked because the invoking agent's tools: whitelist does not include it, the error reads: Agent type 'X' has been denied by permission rule 'Agent(X)' from settings. This points the user toward settings.json permission rules, when the actual restriction lives in the calling agent's tools: frontmatter (a separate, internal validator).

  2. Parser silently degrades non-documented syntax. The official documentation (https://code.claude.com/docs/en/sub-agents section on tools:) shows canonical whitelist syntax as a single Agent(a, b, c) entry. However, multiple-entry notation Agent(a), Agent(b), Agent(c) is silently accepted and interpreted as "only the last Agent(X) wins" (each declaration overrides the previous whitelist set). There is no warning, no hint, no error — the user sees behavior consistent with canonical syntax for one subagent and denial for the others.

Together, these two issues created a diagnostic dead-end that consumed ~4 hours of a single developer's time before an official-doc re-read disambiguated.

Version affected: Claude Code 2.1.118 (likely the entire 2.1.x line — not bisected).


Error Message

2026-04-23T02:22:30.912Z [DEBUG] Applying permission update: Adding 98 allow rule(s) to destination 'projectSettings': ["Agent(alice)", …] 2026-04-23T02:22:37.855Z [INFO] [Stall] tool_dispatch_start tool=Agent toolUseId=toolu_01LXjs… permissionDecisionMs=0 2026-04-23T02:22:37.857Z [WARN] [Stall] tool_dispatch_end tool=Agent toolUseId=toolu_01LXjs… outcome=error durationMs=2 2026-04-23T02:22:37.858Z [DEBUG] Agent tool error (2ms): Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings. 2026-04-23T02:22:37.858Z [ERROR] Error: Error: Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.

Root Cause

Users who write multi-subagent orchestrators typically reach for the notation tools: Agent(a), Agent(b), Agent(c) by analogy with how other comma-separated values work in the same frontmatter line (e.g., Bash(rg:*), Bash(git:*), Bash(wc:*) — each is a separate allowed pattern). The analogy breaks down silently for Agent(...), where the intended semantics is a single allowlist set, not a composable list of patterns.

The misleading error message compounds the problem: it points to the wrong layer entirely. Users begin debugging settings.json permission configuration when the issue is in the calling agent's frontmatter.

In our case (SENTINELLE multi-specialist pipeline with 7 named subagents), we spent approximately 4 hours on a 10-iteration bisect before an official-doc consultation (triggered by a "verify the doc before filing" discipline check) surfaced the canonical syntax. Full timeline: LEARNING 0003 in our internal knowledge base.


Fix Action

Fix / Workaround

2026-04-23T02:22:30.912Z [DEBUG] Applying permission update: Adding 98 allow rule(s) to destination 'projectSettings': ["Agent(alice)", …]
2026-04-23T02:22:37.855Z [INFO]  [Stall] tool_dispatch_start tool=Agent toolUseId=toolu_01LXjs… permissionDecisionMs=0
2026-04-23T02:22:37.857Z [WARN]  [Stall] tool_dispatch_end tool=Agent toolUseId=toolu_01LXjs… outcome=error durationMs=2
2026-04-23T02:22:37.858Z [DEBUG] Agent tool error (2ms): Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.
2026-04-23T02:22:37.858Z [ERROR] Error: Error: Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.

Key observation: permissionDecisionMs=0 — the permission engine itself allowed the call in 0 ms. The denial originates 2 ms later in the Agent-tool dispatch handler (Agent tool error (2ms)), which is a different layer. The error message's "from settings" suffix misleads the user toward permissions.allow/deny inspection.

Code Example

mkdir -p /tmp/cc-syntax-repro/.claude/agents && cd /tmp/cc-syntax-repro

cat > .claude/settings.json <<'JSON'
{"agent":"orchestrator","permissions":{"defaultMode":"bypassPermissions","allow":[]}}
JSON

# Variant A: non-documented multi-declaration syntax
cat > .claude/agents/orchestrator.md <<'MD'
---
name: orchestrator
description: Delegate probes to alice and bob.
tools: Read, Agent(alice), Agent(bob)
model: sonnet
---
Invoke Agent(subagent_type="alice") with prompt "ACK probe", then invoke Agent(subagent_type="bob") with prompt "ACK probe". Report each return verbatim.
MD

cat > .claude/agents/alice.md <<'MD'
---
name: alice
description: probe
tools: Read
model: sonnet
---
Respond with the literal line "ACK alice" and stop.
MD

cat > .claude/agents/bob.md <<'MD'
---
name: bob
description: probe
tools: Read
model: sonnet
---
Respond with the literal line "ACK bob" and stop.
MD

claude "invoke alice and bob and report each result verbatim"

---

2026-04-23T02:22:30.912Z [DEBUG] Applying permission update: Adding 98 allow rule(s) to destination 'projectSettings': ["Agent(alice)",]
2026-04-23T02:22:37.855Z [INFO]  [Stall] tool_dispatch_start tool=Agent toolUseId=toolu_01LXjs… permissionDecisionMs=0
2026-04-23T02:22:37.857Z [WARN]  [Stall] tool_dispatch_end tool=Agent toolUseId=toolu_01LXjs… outcome=error durationMs=2
2026-04-23T02:22:37.858Z [DEBUG] Agent tool error (2ms): Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.
2026-04-23T02:22:37.858Z [ERROR] Error: Error: Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.

---

Error: Agent type 'X' has been denied by permission rule 'Agent(X)' from settings.

---

Error: Agent type 'X' is not in the invoking agent's `tools:` whitelist. Current whitelist: [<the set>]. Reminder: for multiple subagents, use a single `Agent(x, y, z)` entry in `tools:`, not multiple `Agent(x), Agent(y)` entries.

---

Error: Agent type 'X' is not allowed by the invoking agent's `tools:` frontmatter whitelist (not a `settings.json` permission rule).

---

warning: .claude/agents/<name>.md: `tools:` contains multiple `Agent(...)` declarations. Only the last specifier set will apply (e.g., `Agent(a), Agent(b)` ⇒ only `b` is invokable). To whitelist multiple subagents, use a single `Agent(a, b, …)` entry. See https://code.claude.com/docs/en/sub-agents#tools.
RAW_BUFFERClick to expand / collapse

Upstream issue draft — Claude Code 2.1.118: tools: frontmatter — misleading error message + silent acceptance of non-documented Agent(a), Agent(b) syntax

Draft for submission at https://github.com/anthropics/claude-code/issues


Summary

Two related usability issues in the tools: frontmatter parsing of subagent definition files (.claude/agents/*.md). Neither is a parser bug per se — the parser behaves logically — but they compound into a confusing 4-hour debugging session when a user writes a non-documented variant of the Agent(...) syntax:

  1. Error message is misleading. When a subagent invocation is blocked because the invoking agent's tools: whitelist does not include it, the error reads: Agent type 'X' has been denied by permission rule 'Agent(X)' from settings. This points the user toward settings.json permission rules, when the actual restriction lives in the calling agent's tools: frontmatter (a separate, internal validator).

  2. Parser silently degrades non-documented syntax. The official documentation (https://code.claude.com/docs/en/sub-agents section on tools:) shows canonical whitelist syntax as a single Agent(a, b, c) entry. However, multiple-entry notation Agent(a), Agent(b), Agent(c) is silently accepted and interpreted as "only the last Agent(X) wins" (each declaration overrides the previous whitelist set). There is no warning, no hint, no error — the user sees behavior consistent with canonical syntax for one subagent and denial for the others.

Together, these two issues created a diagnostic dead-end that consumed ~4 hours of a single developer's time before an official-doc re-read disambiguated.

Version affected: Claude Code 2.1.118 (likely the entire 2.1.x line — not bisected).


Reproducer

Environment: Ubuntu 24.04 WSL 2, Claude Code 2.1.118 via native installer, defaultMode: bypassPermissions.

mkdir -p /tmp/cc-syntax-repro/.claude/agents && cd /tmp/cc-syntax-repro

cat > .claude/settings.json <<'JSON'
{"agent":"orchestrator","permissions":{"defaultMode":"bypassPermissions","allow":[]}}
JSON

# Variant A: non-documented multi-declaration syntax
cat > .claude/agents/orchestrator.md <<'MD'
---
name: orchestrator
description: Delegate probes to alice and bob.
tools: Read, Agent(alice), Agent(bob)
model: sonnet
---
Invoke Agent(subagent_type="alice") with prompt "ACK probe", then invoke Agent(subagent_type="bob") with prompt "ACK probe". Report each return verbatim.
MD

cat > .claude/agents/alice.md <<'MD'
---
name: alice
description: probe
tools: Read
model: sonnet
---
Respond with the literal line "ACK alice" and stop.
MD

cat > .claude/agents/bob.md <<'MD'
---
name: bob
description: probe
tools: Read
model: sonnet
---
Respond with the literal line "ACK bob" and stop.
MD

claude "invoke alice and bob and report each result verbatim"

Observed:

  • bob (last Agent(X) in tools) returns ACK bob.
  • alice invocation returns: Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.

Expected from the user's perspective (having written both Agent(alice) and Agent(bob)): both subagents are invokable. The user does not read this as two overlapping whitelist declarations — it reads as an additive list.

Correct syntax (per official doc): tools: Read, Agent(alice, bob) — single declaration, both subagents invokable.


Debug log excerpts (CC 2.1.118)

2026-04-23T02:22:30.912Z [DEBUG] Applying permission update: Adding 98 allow rule(s) to destination 'projectSettings': ["Agent(alice)", …]
2026-04-23T02:22:37.855Z [INFO]  [Stall] tool_dispatch_start tool=Agent toolUseId=toolu_01LXjs… permissionDecisionMs=0
2026-04-23T02:22:37.857Z [WARN]  [Stall] tool_dispatch_end tool=Agent toolUseId=toolu_01LXjs… outcome=error durationMs=2
2026-04-23T02:22:37.858Z [DEBUG] Agent tool error (2ms): Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.
2026-04-23T02:22:37.858Z [ERROR] Error: Error: Agent type 'alice' has been denied by permission rule 'Agent(alice)' from settings.

Key observation: permissionDecisionMs=0 — the permission engine itself allowed the call in 0 ms. The denial originates 2 ms later in the Agent-tool dispatch handler (Agent tool error (2ms)), which is a different layer. The error message's "from settings" suffix misleads the user toward permissions.allow/deny inspection.


Why this matters

Users who write multi-subagent orchestrators typically reach for the notation tools: Agent(a), Agent(b), Agent(c) by analogy with how other comma-separated values work in the same frontmatter line (e.g., Bash(rg:*), Bash(git:*), Bash(wc:*) — each is a separate allowed pattern). The analogy breaks down silently for Agent(...), where the intended semantics is a single allowlist set, not a composable list of patterns.

The misleading error message compounds the problem: it points to the wrong layer entirely. Users begin debugging settings.json permission configuration when the issue is in the calling agent's frontmatter.

In our case (SENTINELLE multi-specialist pipeline with 7 named subagents), we spent approximately 4 hours on a 10-iteration bisect before an official-doc consultation (triggered by a "verify the doc before filing" discipline check) surfaced the canonical syntax. Full timeline: LEARNING 0003 in our internal knowledge base.


Proposed fixes (non-exhaustive, either/both welcome)

Fix 1 — disambiguate the error message

Current:

Error: Agent type 'X' has been denied by permission rule 'Agent(X)' from settings.

Proposed:

Error: Agent type 'X' is not in the invoking agent's `tools:` whitelist. Current whitelist: [<the set>]. Reminder: for multiple subagents, use a single `Agent(x, y, z)` entry in `tools:`, not multiple `Agent(x), Agent(y)` entries.

Or minimally:

Error: Agent type 'X' is not allowed by the invoking agent's `tools:` frontmatter whitelist (not a `settings.json` permission rule).

The critical UX win is removing the "from settings" phrase when the source is not settings.

Fix 2 — parse-time warning on non-documented multi-declaration syntax

When parsing tools:, if more than one Agent(...) declaration is encountered (with at least one having a specifier), emit a warning to stderr or to CC's debug log at startup:

warning: .claude/agents/<name>.md: `tools:` contains multiple `Agent(...)` declarations. Only the last specifier set will apply (e.g., `Agent(a), Agent(b)` ⇒ only `b` is invokable). To whitelist multiple subagents, use a single `Agent(a, b, …)` entry. See https://code.claude.com/docs/en/sub-agents#tools.

Ideally this warning surfaces on first invocation of the Agent tool from that file, not only at parse time, so the developer sees it in context.

Fix 3 (optional, nice-to-have) — reject the syntax entirely

If backward compatibility allows, treat multiple Agent(...) declarations as a parse error rather than silently accepting them. This forces the user to the documented syntax. Breaking change, so lower priority.


Related but distinct issues

  • #17853 — TypeError: f.description.split is not a function — distinct (slash-command parser, not subagent tools: frontmatter).
  • #18812 — menu parsing error — distinct.
  • #20264 — bypassPermissions subagent inheritance — distinct layer.
  • #38806 — bypassPermissions 2.1.77+ regressions — distinct.
  • #40580, #34692 — PreToolUse deny not consistently honored on Agent — distinct layer.

None cover the specific UX issues reported here.


Impact

Low-severity in terms of system correctness (the parser behaves logically given its input), but measurable developer-time cost when it bites. For a single developer, 4 hours. For a multi-specialist orchestrator project in early iteration, the impact is proportional to how many agents are being defined. The disambiguation fix is localized and should be cheap to ship.


Acknowledgments

Reported from the SENTINELLE project (private). Full internal investigation trace preserved in .claude/memory/learnings/0001-cc-agent-tools-frontmatter-bug.md (original, partially superseded) and .claude/memory/learnings/0003-tools-frontmatter-syntax-misread.md (corrected after doc audit).

extent analysis

TL;DR

The most likely fix involves updating the error message to accurately reflect the source of the permission denial and potentially adding a parse-time warning for non-documented multi-declaration syntax in the tools: frontmatter.

Guidance

  • Disambiguate the error message: Change the error message to clearly indicate that the permission denial comes from the invoking agent's tools: whitelist, not from settings.json.
  • Add a parse-time warning: When encountering multiple Agent(...) declarations in the tools: frontmatter, emit a warning to inform the user about the correct syntax for whitelisting multiple subagents.
  • Consider rejecting the syntax entirely: If backward compatibility allows, treat multiple Agent(...) declarations as a parse error to enforce the use of the documented syntax.
  • Verify the fix: Test the updated error message and warning by intentionally using the non-documented syntax and checking for the correct error message or warning.

Example

# Correct syntax
tools: Read, Agent(alice, bob)

# Incorrect syntax that should trigger a warning or error
tools: Read, Agent(alice), Agent(bob)

Notes

The proposed fixes aim to improve the user experience by providing clearer error messages and warnings, reducing the time spent debugging issues related to the tools: frontmatter syntax.

Recommendation

Apply the workaround by updating the error message and adding a parse-time warning for non-documented syntax, as these changes are localized and should be easy to implement without introducing significant backward compatibility issues.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING