[BUG] slash-command allowed-tools grants are lost when the run resumes from a task-notification
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
allowed-tools frontmatter on a slash command applies only to the turn the command was issued in, not the session.
If the command launches an async subagent and then ends its turn, the run is resumed by a task-notification — a fresh turn — and every tool granted by the frontmatter is denied from that point onward. Under claude -p there is no permission prompt available, so the denial is terminal.
The permission_denied event carries no decision_reason_type, i.e. it falls through to the default "ask" path rather than matching a deny rule.
The docs describe these grants as applying "while the skill is active", and say a skill's content "stays in the conversation for the rest of the session", which reads as session-scoped.
What Should Happen?
A tool granted by allowed-tools should stay granted for as long as the command is active — including after the run is resumed by a task-notification following an async subagent.
Error Messages/Logs
system/init
Write .tmp-wtest/a.json -> OK
Agent (async) launched
result origin: null
system/init <- run resumed by task notification
Write .tmp-wtest/b.json -> permission_denied
result origin: {"kind": "task-notification"}
{
"type": "system",
"subtype": "permission_denied",
"tool_name": "Write",
"message": "Claude requested permissions to write to .../.tmp-wtest/b.json, but you haven't granted it yet."
}
Steps to Reproduce
- In any trusted project, create
.claude/commands/wtest.md:
```markdown
---
allowed-tools:
- Write
description: permission probe
---
Follow these steps exactly:
- Write the file
.tmp-wtest/a.jsonwith content{"a":1} - Use the Agent tool (subagent_type general-purpose, model haiku) with the prompt:
"Read README.md and summarize it in 3 sentences." Then STOP and end your turn.
Do not make any other tool calls. Wait for the agent to finish.
- Only after the agent's result has come back, Write the file
.tmp-wtest/b.json
with content {"b":2}
```
- Run:
````
echo "/wtest" | claude --print --output-format stream-json --verbose --model haiku
- Observe in the stream: the first
Writesucceeds, a secondsystem/initis emitted when
the subagent completes, and the Write after that is denied. The final result carries
"origin": {"kind": "task-notification"}.
Step 2 of the command matters — the main agent has to end its turn while the subagent is still running. If it keeps working and the subagent finishes underneath it, no resume happens and the bug does not appear. This is why the bug looks intermittent in real use: whether the main agent idles while subagents are outstanding is emergent model behaviour, not something the caller controls.
The reliable diagnostic is the final result event. "origin": {"kind": "task-notification"} present ⇒ the run was resumed and the grants were dropped; field absent ⇒ the run stayed in its original turn and the grants held.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.228 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Non-interactive/CI environment
Additional Information
Controls — same command, same version, one variable changed each time:
| Variant | Result |
|---|---|
| No Agent call; three writes in a row | all three succeed |
| Agent launched, but the main turn does not end before the later writes | all succeed |
| Frontmatter grant removed, --allowedTools "Write" passed on the CLI instead | write after the resume succeeds |
So the grant is turn-scoped, the async-agent resumption starts a new turn, and CLI/settings grants are unaffected.
Versions. Reproduced identically on 2.1.226 and 2.1.228, so this is not a recent regression.
Environments. Reproduced on Windows 11 and on Azure DevOps ubuntu-latest hosted agents. Model-independent: observed with Opus in CI, reduced repro above uses Haiku.
Two runs of the same command, same version family, opposite outcomes — which is what makes this hard to spot:
| | Passing run | Failing run |
|---|---|---|
| CLI version | 2.1.226 | 2.1.228 |
| num_turns | 30 | 13 |
| origin on final result | absent | {"kind": "task-notification"} |
| Write after the review fan-out | succeeded | denied |
| Comments posted | yes | no |
Same command file, same frontmatter, same repo. The only structural difference is that the failing run idled while its four review subagents ran and was resumed by a task-notification; the passing run stayed inside its original turn the whole way. Nothing the caller controls decides which happens.
Impact. Any command that declares its tools in frontmatter and fans out to subagents before doing its real work is broken in headless mode. Ours is an Azure DevOps PR-review command: it runs four parallel review subagents, then posts findings using Write (to build REST payload files) and Bash(az devops invoke *) — both frontmatter-only grants. The review completes correctly and then cannot post anything.
The failure is quiet:
- the CLI exits 0 and the pipeline reports success
- the only signal is the
permission_denialsarray in the result JSON - the model, told it may work around denials with other tools, spends several turns trying alternatives before giving up
One such run cost $8.74 and 233s to produce a correct review that went nowhere.
Possibly related: #76196 reports what may be the same root cause from the TUI ("granted exactly once per session, then re-prompts on identical subsequent calls"). If each subsequent invocation there landed in a new user turn, both reports reduce to the same turn-scoping behaviour.
Workaround. Pass the same patterns via --allowedTools or a settings file; those are session-scoped and survive the resume.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗