Custom subagents that declare Bash in frontmatter never receive it (no config can restore it)
Title
Custom subagents that declare a shell tool (Bash) in frontmatter never receive it — silently, with no config able to restore it
Summary
A custom subagent defined in .claude/agents/*.md with Bash listed in itstools: frontmatter is invoked with that tool silently omitted. Every other
declared tool (Read, Write, Edit, Grep, Glob) is granted correctly —
only the shell tool is missing. There is no error, warning, or denial message;
the subagent simply reports (and behaves as if) it never had the tool. This
makes any subagent whose job requires running a process (tests, git diff,
linting, etc.) non-functional as a native subagent type, with no known
workaround via permissions or settings.
Environment
- Claude Code version: 2.1.263
- Reproduced identically on two independent surfaces:
- VS Code extension (
anthropic.claude-code-2.1.263-win32-x64) - Standalone CLI (
npm install -g @anthropic-ai/claude-code, same
version, run from a separate terminal process with no VS Code
involved)
- OS: Windows 11 Home (10.0.26200), PowerShell 5.1 — the platform's shell
tool is named PowerShell rather than Bash here, but the agent
frontmatter's declared tool name is Bash either way (see "ruled out"
below for why this isn't a naming issue)
- Project has no
.claude/settings.jsondeny/ask rules of any kind at the
time this was first observed (file didn't exist yet); later testing added
one with an explicit permissions.allow list (see below) with no effect
Repro steps
- Define a custom subagent in
.claude/agents/example.md:
``yaml``
---
name: example
description: ...
tools: Bash, Grep, Glob
model: ...
---
...instructions...
- Invoke it as its real native subagent type (via the
Agent/Task-style
tool with subagent_type: "example", or the CLI's equivalent) with a
diagnostic prompt: *"Report the exact, complete list of tool names
available to you right now, verbatim."*
- Observe the response.
Expected
The subagent reports having Bash (or the platform's shell-tool
equivalent), Grep, and Glob — matching its declared tools: frontmatter.
Actual
The subagent reports only Grep, Glob. Bash is absent — not denied when
attempted, simply never listed as available at all.
What we ruled out
- Naming mismatch (
Bashvs. this platform's actual shell-tool name,
PowerShell): renamed the frontmatter's declared tool from Bash to
PowerShell and re-tested — no change, still absent. Reverted.
- Permissions/approval gap (the working theory going in, since a
classifier-style denial message elsewhere in the product explicitly
suggested "add a Bash permission rule to your settings"): created
.claude/settings.json with a permissions.allow list covering every
shell-command pattern the affected agents actually use (git diff,
git rev-parse, npx playwright test, python -m pytest, node, plus general read-only commands), re-ran the diagnostic —
--check
no change. Also re-tested after a full VS Code window reload, to rule
out stale/cached settings — no change. This is the key evidence it's
not a permissions issue: an approval gap would show the tool present
but a specific call getting denied; what's actually observed is the
tool never being offered to the subagent's toolset in the first place,
which permissions.allow has no power over.
- Sandbox restriction: checked
sandbox.enabledand related settings
(claude-code-settings.schema.json's sandbox.* block) across project,
user, and any managed/policy settings location on the machine — none
set anywhere, no relevant env vars (CLAUDE_CODE_SANDBOX, etc.) set
either.
- VS Code-extension-specific bug: installed the standalone CLI (same
version) and ran the identical diagnostic as a separate process —
identical result (Grep, Glob only). Rules this out as an integration
bug specific to the extension's subagent-spawning code path.
Impact
Confirmed across all 6 subagents in a real multi-agent QA pipeline project:
- 2 of 6 agents are completely non-functional as native subagent types
(both need to run processes — git diff, test runners — with no
Grep/Glob-only substitute for either).
- 1 of 6 degrades gracefully (loses a lint/syntax-check step, everything
else still works).
- 3 of 6 are unaffected (never needed shell execution for their actual
work).
Current workaround for the affected agents: the orchestrator pre-computes
whatever the subagent would have needed shell access for (e.g. runninggit diff itself and handing the subagent a pre-computed file list) where
that's possible, or falls back to simulating the subagent via the
general-purpose agent type fed the same instructions where it isn't (e.g.
running actual tests) — the general-purpose agent type is not affected
by this restriction and receives * (all tools) normally. This works but
defeats the purpose of having a scoped, single-responsibility custom
subagent for that role.
Question for Anthropic
Is this intentional (e.g., a deliberate policy that a custom subagent
invoked without the interactive session present can never be granted
process-execution capability, regardless of static declaration or
settings), or is it a bug in how a spawned subagent's toolset is
constructed? If intentional, is there a supported way to grant shell
access to a custom subagent at all — and if not, that's worth stating
explicitly in the subagent-authoring docs, since right now tools: Bash
in frontmatter silently does nothing.