claude-code - 💡(How to fix) Fix [BUG] Sub-agent frontmatter `tools:` array silently drops first and last positions at spawn time

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…

Error Message

When the spawned sub-agent attempts to call a dropped tool, the SDK returns these exact error strings:

For Read at position 1 of [Read, Write, Bash]: Error: No such tool available: Read. Read exists but is not enabled in this context. Use one of the available tools instead.

For Bash at last position of [Read, Write, Bash]: Error: No such tool available: Bash. Bash exists but is not enabled in this context. Use one of the available tools instead.

The wording "exists but is not enabled in this context" is consistent across the dropped tools — Claude Code recognizes the tool name but has disabled it for this specific sub-agent context.

Root Cause

  • Any plugin sub-agent declaring exactly 3 tools loses 2 of them at runtime, keeping only the middle one (typically Write).
  • Any sub-agent declaring exactly 2 tools loses both — meaning a plugin that wires tools: [Read, Write] for a critic role gets a runtime agent with 0 usable declared tools.
  • Sub-agent patterns that rely on Read for hypothesis-blind quote-before-assess critic discipline, or on Bash for integrity checks via jq / grep, are silently broken.
  • The failure is hard to detect because the agent self-reports "tool not available" mid-completion but often goes on to produce narrative output that looks like a normal verdict to upstream callers.

Fix Action

Workaround

Pad the tools: array with throwaway built-in tools at position 1 and last position. Suggested padding: Glob and Grep (both real Claude Code built-in tools, so YAML parses cleanly, and harmless if dropped):

tools: [Glob, Read, Write, Bash, Grep]

The real tools (Read, Write, Bash) now occupy middle positions 2/3/4 and survive the drop.

Code Example

When the spawned sub-agent attempts to call a dropped tool, the SDK returns these exact error strings:

For Read at position 1 of [Read, Write, Bash]:
    Error: No such tool available: Read. Read exists but is not enabled in this context. Use one of the available tools instead.

For Bash at last position of [Read, Write, Bash]:
    Error: No such tool available: Bash. Bash exists but is not enabled in this context. Use one of the available tools instead.

The wording "exists but is not enabled in this context" is consistent across the dropped tools — Claude Code recognizes the tool name but has disabled it for this specific sub-agent context.

---

name: test-agent
description: Test agent demonstrating tools-array first/last drop bug.
tools: [Read, Write, Bash]
disallowedTools: [Task]Role: test-agentProbe stub. Execute exactly what the spawn prompt requests.

---

(Padding: `Edit` at position 1, `WebFetch` at position 5/last. Real targets `Read`/`Write`/`Bash` move to middle positions 2/3/4.)

   Quit Claude Code completely (cmd+Q — plugin agent definitions are loaded at session start, mid-session disk edits do NOT take effect) and relaunch. Re-spawn with the same prompt.

6. Observe: `Read`, `Write`, AND `Bash` all fire end-to-end. `Edit` (pos 1) and `WebFetch` (pos 5) are dropped instead. Bash returns `stdout: test-ok, exit 0`.

7. **Stronger confirmation with 7-item list**: Set frontmatter to `tools: [Edit, Read, Write, Bash, WebFetch, NotebookEdit, WebSearch]`. After quit+relaunch, observe runtime exposed set is `{Read, Write, Bash, WebFetch, NotebookEdit}` (positions 2-6). `Edit` (pos 1) and `WebSearch` (pos 7) are dropped.

Result summary across 3 array sizes (same agent file, same plugin, same session, only `tools:` array order changed):

| List size | Declared `tools:` | Exposed at runtime | Dropped (positions) |
|---|---|---|---|
| 3 | `[Read, Write, Bash]` | `{Write}` | Read (1), Bash (3=last) |
| 5 | `[Edit, Read, Write, Bash, WebFetch]` | `{Read, Write, Bash}` | Edit (1), WebFetch (5=last) |
| 7 | `[Edit, Read, Write, Bash, WebFetch, NotebookEdit, WebSearch]` | `{Read, Write, Bash, WebFetch, NotebookEdit}` | Edit (1), WebSearch (7=last) |

Three independent data points all match the first-and-last drop rule. Spawn 5 is the falsification test: moving `Read` and `Bash` off the array edges restored both tools at runtime.

I can provide my private repo to you for reproducing.

### Claude Model

Opus

### Is this a regression?

I don't know

### Last Working Version

Untested against earlier Claude Code versions; I cannot confirm whether the bug is new in 2.1.x or has existed longer.

### Claude Code Version

2.1.143 (Claude Code)

### Platform

Anthropic API

### Operating System

macOS

### Terminal/Shell

Terminal.app (macOS)

### Additional Information

## Workaround

Pad the `tools:` array with throwaway built-in tools at position 1 and last position. Suggested padding: `Glob` and `Grep` (both real Claude Code built-in tools, so YAML parses cleanly, and harmless if dropped):
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

Claude Code silently drops the first and the last items from a plugin sub-agent's frontmatter tools: array at spawn time. Only the middle positions survive in the agent's runtime tool inventory.

For example, an agent declaring tools: [Read, Write, Bash] (3 tools) is spawned with access to {Write} only — Read and Bash are silently removed from its function inventory. When the agent attempts to call them, the SDK returns Error: No such tool available: <Tool>. <Tool> exists but is not enabled in this context.

The drop is deterministic, reproducible across sessions and fresh Claude Code launches, and depends only on array position — not on which specific tools are at those positions, not on agent body size, not on hooks, not on plugin settings, not on session permissions.

What Should Happen?

All tools declared in a sub-agent's frontmatter tools: array should be present in that agent's runtime tool inventory. An agent declaring tools: [Read, Write, Bash] should be able to invoke all three. Plugin authors should not have to reason about array positions, nor pad their tools: arrays with throwaway tools as bookends, to get their declared tools to work.

Error Messages/Logs

When the spawned sub-agent attempts to call a dropped tool, the SDK returns these exact error strings:

For Read at position 1 of [Read, Write, Bash]:
    Error: No such tool available: Read. Read exists but is not enabled in this context. Use one of the available tools instead.

For Bash at last position of [Read, Write, Bash]:
    Error: No such tool available: Bash. Bash exists but is not enabled in this context. Use one of the available tools instead.

The wording "exists but is not enabled in this context" is consistent across the dropped tools — Claude Code recognizes the tool name but has disabled it for this specific sub-agent context.

Steps to Reproduce

  1. Create a plugin with one sub-agent file at plugins/auto-research/agents/test-agent.md:
name: test-agent
description: Test agent demonstrating tools-array first/last drop bug.
tools: [Read, Write, Bash]
disallowedTools: [Task]Role: test-agentProbe stub. Execute exactly what the spawn prompt requests.
  1. Register the plugin via marketplace JSON or enabledPlugins in ~/.claude/settings.json, then launch Claude Code in a project with this plugin loaded.

  2. From the main session, spawn the sub-agent via the Task tool:

    • subagent_type: auto-research:test-agent
    • prompt: Step 1: call Bash with command "echo test-ok" and report stdout. Step 2: call Read on any existing file and quote first line. Step 3: call Write to "/tmp/test-out.txt" with content "ok". Report which tools fired and the exact SDK error string for any failure.
  3. Observe: Bash and Read calls return the "tool exists but is not enabled" error. Only Write fires successfully.

  4. Falsification test: Edit the agent's frontmatter to:

yamltools: [Edit, Read, Write, Bash, WebFetch]

(Padding: Edit at position 1, WebFetch at position 5/last. Real targets Read/Write/Bash move to middle positions 2/3/4.)

Quit Claude Code completely (cmd+Q — plugin agent definitions are loaded at session start, mid-session disk edits do NOT take effect) and relaunch. Re-spawn with the same prompt.

  1. Observe: Read, Write, AND Bash all fire end-to-end. Edit (pos 1) and WebFetch (pos 5) are dropped instead. Bash returns stdout: test-ok, exit 0.

  2. Stronger confirmation with 7-item list: Set frontmatter to tools: [Edit, Read, Write, Bash, WebFetch, NotebookEdit, WebSearch]. After quit+relaunch, observe runtime exposed set is {Read, Write, Bash, WebFetch, NotebookEdit} (positions 2-6). Edit (pos 1) and WebSearch (pos 7) are dropped.

Result summary across 3 array sizes (same agent file, same plugin, same session, only tools: array order changed):

List sizeDeclared tools:Exposed at runtimeDropped (positions)
3[Read, Write, Bash]{Write}Read (1), Bash (3=last)
5[Edit, Read, Write, Bash, WebFetch]{Read, Write, Bash}Edit (1), WebFetch (5=last)
7[Edit, Read, Write, Bash, WebFetch, NotebookEdit, WebSearch]{Read, Write, Bash, WebFetch, NotebookEdit}Edit (1), WebSearch (7=last)

Three independent data points all match the first-and-last drop rule. Spawn 5 is the falsification test: moving Read and Bash off the array edges restored both tools at runtime.

I can provide my private repo to you for reproducing.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

Untested against earlier Claude Code versions; I cannot confirm whether the bug is new in 2.1.x or has existed longer.

Claude Code Version

2.1.143 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Workaround

Pad the tools: array with throwaway built-in tools at position 1 and last position. Suggested padding: Glob and Grep (both real Claude Code built-in tools, so YAML parses cleanly, and harmless if dropped):

tools: [Glob, Read, Write, Bash, Grep]

The real tools (Read, Write, Bash) now occupy middle positions 2/3/4 and survive the drop.

Impact

  • Any plugin sub-agent declaring exactly 3 tools loses 2 of them at runtime, keeping only the middle one (typically Write).
  • Any sub-agent declaring exactly 2 tools loses both — meaning a plugin that wires tools: [Read, Write] for a critic role gets a runtime agent with 0 usable declared tools.
  • Sub-agent patterns that rely on Read for hypothesis-blind quote-before-assess critic discipline, or on Bash for integrity checks via jq / grep, are silently broken.
  • The failure is hard to detect because the agent self-reports "tool not available" mid-completion but often goes on to produce narrative output that looks like a normal verdict to upstream callers.

Alternative causes ruled out during debugging

  • Total system prompt size cap: agent body size varied from ~700 bytes (stub) to ~19 KB (full role spec) with no change to the drop pattern. Smaller body did not save additional tools; larger body did not lose additional tools beyond first+last.
  • Plugin manifest gating: .claude-plugin/plugin.json only declares name / description / version. No tools allowlist.
  • Project permissions allowlist: adding bare tool names ("Read", "Bash", etc.) to permissions.allow in .claude/settings.local.json did not restore the dropped tools.
  • Hook output override: grepped every SubagentStart hook in the plugin; only hookSpecificOutput.additionalContext and permissionDecision are emitted. No tool grant override mechanism in any hook.
  • Agent body content as trigger: stripping the body to a one-line stub did not change the drop pattern; same first+last positions still dropped.
  • YAML frontmatter parsing edge case: the agent files have exactly two --- delimiters (frontmatter open + close), zero --- in body. Standard YAML parse should work.
  • disallowedTools field interaction: the field contains only [Task] and appears honored correctly. Likely a separate code path from the tools field.

Suggested fix

The frontmatter tools: array should be passed through to the spawned sub-agent's runtime tool inventory unchanged. The first-and-last truncation appears to be an off-by-one or array-handling bug somewhere in the harness layer that constructs the sub-agent's tools array before the API request.

If first/last positions are intentionally reserved for some internal purpose, please:

  1. Document this behavior in the plugin sub-agent frontmatter reference.
  2. Emit a warning at plugin load time when an agent declares fewer than 3 tools (since 2 declared = 0 usable under the current rule, and 1 declared = 0 usable).
  3. Ideally provide a way to declare tools that bypasses the drop entirely.

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

claude-code - 💡(How to fix) Fix [BUG] Sub-agent frontmatter `tools:` array silently drops first and last positions at spawn time