Skills/subagents do not inherit user-level permissions from settings.json
Open 💬 29 comments Opened Jan 18, 2026 by thomast73
User-level permissions configured in ~/.claude/settings.json under permissions.allow are not being inherited by skills/subagents. All bash commands require permission prompts when executed within a skill, even though those same commands are auto-approved when used in the main conversation.
Steps to Reproduce
- Configure user-level permissions in
~/.claude/settings.json:
{
"permissions": {
"allow": [
"Bash(test)",
"Bash(git rebase)",
"Bash(mvn verify)",
"Bash(grep)",
"Bash(head)",
"Bash(lsof)",
"Bash(sleep)",
"Bash(tail)",
"Bash(echo)"
]
}
}
- Restart Claude Code to load permissions
- Verify permissions work in main conversation (e.g.,
lscommand runs without prompt) - Run a skill that uses these commands
- Observe: All bash commands prompt for permission despite being in the allow list
Expected Behavior
Skills should inherit user-level permissions from settings.json. Commands in the permissions.allow list should be auto-approved without prompting.
Actual Behavior
Skills prompt for permission on every bash command. Example commands that prompted:
- test -f pom.xml
- grep with head pipeline
- git rebase team/master
- mvn spring-boot:run
- lsof commands
- sleep and tail commands
Environment
- Claude Code version: 2.1.12
- OS: macOS 26.2 (25C56)
- Settings file: ~/.claude/settings.json
Workaround
Select option 2 to create workspace-level permissions, but this defeats the purpose of user-level permissions.
Impact
Significantly degrades user experience when using skills, requiring manual approval of dozens of commands.
29 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Additional Evidence: Workspace Permissions Not Matching in Skills
I've discovered that workspace-level permissions in
.claude/settings.local.jsonare not matching commands even when they should, specifically in skills/subagents.Commands That Prompted (Without Option 2)
These commands prompted with only "Yes" or "No" options - no workspace-level "don't ask again" option:
``
bash
`mvn spring-boot:run -pl app > /tmp/tls-server.log 2>&1 & echo $\!
Bash(mvn spring-boot:run:*)`**Should match:**
``
bash
`SPRING_JAVA_PID=""; for i in {1..60}; do SPRING_JAVA_PID=$(pgrep -P 36034 -f java 2>/dev/null); if [ $? -eq 0 ] && [ -n "$SPRING_JAVA_PID" ]; then echo "Server Java process found: $SPRING_JAVA_PID"; exit 0; fi; sleep 1; done; echo "Timeout: No Java process found"
Bash(for i in {1..60}:*)`**Should match:**
Command That Offered Option 2
This command correctly offered workspace-level approval:
No pattern in workspace permissions - so it correctly offered to add one.
Current Workspace Permissions
.claude/settings.local.jsoncontains:Analysis
The patterns should match but don't, suggesting:
>,2>&1), pipes, and background operators (&) may break pattern matchingSPRING_JAVA_PID="") may prevent matching:*wildcard syntax in workspace permissions behaves differently than expectedThis appears distinct from the duplicate issues mentioned (#10906, #11934, #18172), as this is specifically about workspace permission pattern matching in skills/subagents, not about permissions being ignored entirely.
I was able to use the
:*legacy syntax as a workaround. Let's hope the legacy syntax sticks around until this issue is fixed 😅.e.g.,
Adding Windows-specific evidence. On Windows this issue compounds with a path format mismatch that makes allow rules unreachable for subagents even when correctly configured.
Setup:
defaultMode: "acceptEdits",--add-dir "C:\Users\david", rules likeRead(C:/Users/david/**)in~/.claude/settings.json.What happens: Main session file ops auto-allow. Subagent file ops prompt every time — two reasons stacking:
acceptEditsscope (including--add-dir) not inherited (the core bug here)C:\Users\david\...) which don't match forward-slash rules (C:/Users/david/**) — no normalization before rule matchingForensic data from 9 days of PreToolUse + PermissionRequest hook logging (303 permission events, 222 file-tool ops):
//c/Users/david/...) — that matches neither the settings.json rules nor the paths Claude generatesEven with duplicate rules in both
C:/and/c/format (36 rules total) and the broadest possible--add-dir, subagents still prompt because they don't inheritacceptEditsscope and the path normalization gap blocks rule matching.Related Windows path issues: #12796, #10337, #22576
Reproducing on macOS — global settings.json allow rules ignored by subagents
Platform: macOS (Darwin 25.3.0)
Claude Code version: claude-sonnet-4-6 (desktop app)
Setup
~/.claude/settings.jsonhas all common tools in the top-levelallowlist:There is no project-level
.claude/settings.jsonoverriding the global config.Observed behavior
Top-level session:
Grep,WebFetch,WebSearch,Bashall auto-allow as expected.When Claude spawns a subagent via the
Agenttool, those same tools prompt for permission on every call — as ifsettings.jsonis not being read at all by the subprocess.Workaround
Perform searches/research directly in the top-level conversation (where runtime grants are in effect) rather than delegating to subagents.
I've also found this to be inconsistent. Sometimes it's fine and the subagent processes requests fine but other times it requires explicit approval
Also running into this issue constantly. Most annoying...
@tzulingk @nekovin @pmolodo
Out of curiosity, does it still happen if you run the agent in the background (CTLR+B by default)?
(though I don't know if that's possible in Claude Code via Claude Desktop)
I seemed to get better result in background mode on a similar issue, but I'm not sure if it's a reliable workaround for now, or if I'm missing something.
Happens all the time. Super annoying.
Hooks in
~/.claude/settings.json(user-level) do inherit to subagents and skills — unlike permission rules which may not.If you need safety guarantees across all agents:
This writes hooks to
~/.claude/hooks/and registers them in user-level~/.claude/settings.json. Since hooks fire before every tool call regardless of which agent instance is running, they effectively enforce permissions that the permission system fails to inherit.Verified behavior: hooks in user settings fire for main agent, subagents (Task/Agent tool), and skills. The hook process inherits the parent's settings.json path.
I traced the permission flow through the minified CLI source (v2.1.83) and found the exact bug. Here's a
writeup.
The Bug
Inside MN(), the function that spawns subagents, there's a getAppState wrapper that rebuilds
alwaysAllowRules when the subagent has allowedTools defined:
// In MN()'s getAppState wrapper (P = allowedTools parameter)
When allowedTools is passed, the code creates a new alwaysAllowRules object with only two categories:
cliArg (preserved from parent) and session (set to the allowedTools array). Every other category is
silently dropped:
never be droppable)
The full set of categories is defined as:
The flatten helpers (jE6, _76, HE6) iterate over all N6A categories using || [] for missing keys, so
dropped categories are silently ignored. No error, just missing rules.
Who Triggers It
Only one call site passes allowedTools to MN: the inProcessRunner (the teammate/in-process agent runner).
Regular Agent/Task tool subagents construct the a object without allowedTools, so P is undefined and the
buggy block doesn't execute for those.
This means the bug specifically affects in-process teammates and any path that provides allowedTools to MN.
Regular Agent(subagent_type="general-purpose") calls should inherit permissions correctly, though users
may still see prompts for other reasons (mode differences, shouldAvoidPermissionPrompts auto-denying in
async agents, etc.).
The Fix Already Exists in the Codebase
The Skill tool's contextModifier does the exact same operation correctly:
// Skill tool contextModifier (CORRECT)
It spreads the existing rules and only adds to the command category.
Suggested Fix
// Before (buggy):
// After (fixed):
One-line change: spread all parent categories instead of cherry-picking cliArg. Deny and ask rules are
already preserved via the ...w6 spread above the block, so there's no security regression. On the contrary,
this restores policySettings to the allow rules, which should never have been droppable.
Why Reports Are Inconsistent
Multiple distinct problems are being reported under this issue:
be silently denied (not prompted). Rules are inherited correctly, but anything not in the allow list is
auto-rejected instead of asking the user.
auto), changing which commands get auto-approved by the classifier vs prompted.
This would explain why some users see it "all the time" (using teammates/skills with allowedTools) while
others see it inconsistently (hitting different code paths).
I run a skill that spawns 6+ subagents in parallel via the Agent tool. The skill defines allowed-tools in its YAML front matter, which means it always hits the buggy allowedTools branch @zendevio identified.
Key data point: 6 identical subagents, same config, same prompt, same Write target — 4 succeed, 2 get permission-denied. Same session, same ~/.claude/settings.json. Not a config problem.
@zendevio's root cause explains every behavior I've seen. Skills with allowed-tools always trigger the broken path — that's why this is consistent for skill authors and intermittent for others.
Consolidating related issues that are all downstream of this:
Would love to see the one-line spread fix prioritized. This has been open since January and makes subagent file I/O essentially unusable for skills.
Hooks in
~/.claude/settings.jsondo propagate to subagents and skills — unlike permission patterns. This makes hooks the workaround for this exact problem.Hooks defined in
~/.claude/settings.jsonare loaded at the process level for every Claude Code invocation — including subagent spawns. Thepermissions.allowpatterns have a separate resolution path that doesn't always inherit correctly for skill/subagent contexts.Put this in
~/.claude/settings.json:Your
mvn spring-boot:run,git rebase,grep | headpipelines will all auto-approve in skills without the workspace-level workaround.+1 — specific use case where this bites hard.
Scenario: large multi-package Swift refactor (~40 file writes across three packages) delegated to a Task subagent for context-budget reasons (the dispatching agent had accumulated significant prior-phase context that didn't fit cleanly into the implementation work).
Friction: parent session had
Bash(*)allowed at the user level;Write/Editwere intentionally not allow-listed (interactive approval per invocation, fine for the parent). The Task subagent didn't inherit even the parent's path-scopedEdit(/path/**)allows from.claude/settings.local.json. Faced with 40+ approval prompts mid-task, the subagent fell back tocat <<EOF > file.swiftheredocs via Bash — the only pre-approved write path.Why the heredoc fallback isn't scalable: breaks on content containing
EOFtokens or escape-sensitive characters; files >200 LOC are noisy in history; losesEdit's targeted-diff review; bypassesWrite's content-as-argument safety check.Practical workaround we adopted: don't dispatch subagents for file writes; do all writes in the main session. Works but forfeits the context-isolation benefit that motivated subagent dispatch — for large refactors, that's a real cost.
Path-scoped allow inheritance (subagents see parent's
.claude/settings.local.jsonallows for paths the parent has authorized) would resolve this cleanly without changing the security boundary.Adding a Max plan user data point as of 2026-04-22 — this is still blocking real workflows.
My setup: Running an orchestration workspace with 11+ specialist subagents (each defined in .claude/agents/*.md with
tools: that explicitly lists Bash). Parent session has ~/.claude/settings.json with permissions.allow including
Bash(*). Subagents launched via the Agent tool with run_in_background: true still return permission-denied errors when
they try to run Bash — even though the parent would run the same command without a prompt.
Observed behavior is the same as described in this issue and in the now-closed #25526 and #27661, which suggests the
subagent-specific variant of the inheritance bug is still live and should be tracked here.
Practical impact: every subagent dispatch that hits a Bash tool path becomes a wasted round-trip — the orchestrator
has to pick up the work the specialist was supposed to execute. That's the opposite of what multi-agent orchestration
is supposed to enable, and it makes the per-agent permission-block workaround (adding permissions.allow to every agent
definition file) the only option, which is fragile — every new agent needs to remember the block.
What would help:
better one).
per-agent duplication.
Happy to provide a reproduction workspace if that's useful.
— Sonia Martinez, Max plan subscriber, running Claude Code CLI on macOS 15.4
I am developing an autonomous agent group which needs limited amount of tools (running things like git, gh, semgrep etc in bash). The workaround was to implement custom mcp servers for these bash calls.
Really blocking my workflow for subagents generating specifically structured file as output.
Reproduces on Windows (native), not just macOS — same root cause.
Setup: project .claude/settings.json has Write(<absolute-windows-path>\\**) in permissions.allow. Both the parent and a Task-tool subagent (custom agent, tools: Write in frontmatter) call Write from the same cwd with identical permission_mode: "default".
Instrumented with a PreToolUse hook on Write logging the full tool_input JSON.
Parent Write call → file_path matches the glob byte-for-byte → OK.
Subagent Write call → file_path matches the same glob byte-for-byte → "Permission to use Write has been denied."
Only structural difference in the hook input: subagent invocation has agent_id and agent_type populated, parent's does not. The rule is correctly configured and matched by the parent's resolver — the subagent's resolver doesn't see permissions.allow. Confirms the title of this issue on Windows.
Workaround that worked on 2.1.126:
Project-level
.claude/settings.jsonhad"Write(md/code-reviews/**)"— Task-tool sub-agent's Write was being denied (the bug). Adding the same rule to.claude/settings.local.json(local-level, normally gitignored) makes the sub-agent inherit it — Write succeeds deterministically.Today I started experiencing this problem on steroids. Custom sub-agents ar not only is not using the user-level permission but are directly getting reject to do task they should be allowed to perform (name write markdown files) Tried @android6 workaround without effect
Setup
.claude/settings.jsonallowsWrite(.claude/**/*.md)(andWrite(**/*.md),Write(**/.claude/**/*.md))..claude/journals/...and.claude/_perm_probe.mdsucceed without prompts. Rule is active..claude/agents/) invoked via the Agent tool: everyWriteto the same paths returns the literal stringPermission to use Write has been denied.from the tool, with no user prompt surfaced.Tried
permissionMode: acceptEdits— nope, denied.permissionMode: bypassPermissions— nope, deniedWrite(...)rules into.claude/settings.local.json—nope, denied event in a fresh session.haikuandsonnetmodel overrides on the sub-agent — same behavior.+1 — and worth flagging the symmetric failure on the deny side, which is where this stops being a UX issue and starts being a security issue.
Same root cause: user-level settings.json permissions don't propagate to subagents. But for permissions.deny rules, the failure mode is silent — there's no prompt, the agent just reads files the parent config explicitly forbade.
Concrete example: a deny rule for Read(./.env) and Read(./.env.*) in ~/.claude/settings.json correctly blocks the main session from reading .env. Subagents read it anyway. Users who have correctly hardened their config believe .env is protected and are silently wrong.
The allow-side bug is a papercut. The deny-side bug is a footgun for anyone using Claude Code in repos with secrets.
Fix should cover both: subagents inherit the full parent permission hierarchy (allow + deny) by default.
One more pointer: I tried removing the settings.json file and the confirmation prompts came back. I moved my permissions list to settings.local.json and removed the settings.json file. Subagents seems to start requesting for access still, but I rather prefer this behavior than just failing. Clearly there are some issues with the permissions
This should be considered a blocker and a security issue, has having to confirm, every times, hundreds of operations is prone to errors and could lead to a wrong confirm
Reporting an adjacent case that may share a root cause: #58652.
Same symptom (
Bash(ls *)/Bash(cat *)etc. inpermissions.alloware silently ignored, while rules for non-built-in commands likeBash(jq *)work), but the trigger isclauderather than a dispatched subagent or skill.--agent <name>
Per the binary,
--agentmode runs the named agent as the main session (not as a subagent), so it's a different code path from this issue, but the failure mode looks identical. Posting in case it's useful evidence that the bug isn't subagent-specific but rather affects any non-default agent context.I think Anthropic needs to re-think this permission system. It's a mess.
Agree, and as wrote almost everywhere, this should be considered a CRITICAL and a BLOCKER bug, as having to approve almost every command, tens/hundreds of commands every times generate a lot of noise and dangerous command could be approved wronhly just because you had to approve 23 times in a row a simple "php -l" or any other syntax check.
This bug seems to also cause my user-level
"disableAutoMode"setting to be ignored. A subagent was able to execute WebFetch without a permission prompt, even though I have NOT allowlisted that tool, I have NOT opted into auto-mode, and I am NOT running bypass/dangerously skip permissions. Related: https://github.com/anthropics/claude-code/issues/64162Only work-around seems to be authorizing via the user-level permissions
allowin~/.claude/settings.jsonNope, i'm using exactly that and is followed only by the main agent, subagents doesn't follow the auth rules in that file