[BUG] allowedTools `Bash(printf *)` denies `printf x > file` with no message that output redirection defeated the prefix rule
Status Open
Reported on v2.1.273
Maintainer reply None cached
Activity 0 comments · opened Sep 16, 2026
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (nearest: #94314 covers
;/&&compound-command matching in allow rules; this is the output-redirect case, and specifically the missing explanation in the denial) - [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
With --allowedTools 'Bash(printf *)', the plain command printf x runs, but printf x > redirected.txt is denied, and nothing in the output says the redirect is why. The permission_denials entry names the tool and command only. From the outside the denial is indistinguishable from a missing or malformed allow rule, so operators running unattended sessions have no signal what to change.
Repro, in an empty scratch directory, default login, project settings only so user settings add no permissions:
env -u ANTHROPIC_API_KEY claude -p --output-format json --model sonnet \
--setting-sources project --allowedTools 'Bash(printf *)' --permission-prompts none \
'Use Bash to run exactly: printf x > redirected.txt. Then answer CREATED if the tool ran, DENIED if the tool was denied.' </dev/null
env -u ANTHROPIC_API_KEY claude -p --output-format json --model sonnet \
--setting-sources project --allowedTools 'Bash(printf *)' --permission-prompts none \
'Use Bash to run exactly: printf x. Then answer OUTPUT followed by the stdout if the tool ran, DENIED if the tool was denied.' </dev/null
What Should Happen?
Either:
Bash(printf *)covers a command string that starts withprintf, redirect included; or- the denial says the rule did not match because shell redirection is treated specially, naming the attempted rule (
Bash(printf *)) and the redirect form that made it unsafe, both in thepermission_denialsentry and in the human-readable denial message. The matcher's semantics for shell operators and redirection should also be documented.
If redirects intentionally require a separate rule, that is fine, but the denial has to say so.
Error Messages/Logs
# redirected form, same rule: denied, file not created
{"subtype":"success","is_error":false,"terminal_reason":"completed","num_turns":2,"permission_denials":[{"tool_name":"Bash","tool_input":{"command":"printf x > redirected.txt","description":"Write x to redirected.txt"}}],"result":"DENIED"}
# ls redirected.txt -> does not exist
# plain form, same rule: runs
{"subtype":"success","is_error":false,"terminal_reason":"completed","num_turns":2,"permission_denials":[],"result":"OUTPUT x"}
Environment
- Claude Code 2.1.273 (reproduced), macOS 15
- Model: sonnet
- Context: headless sessions with narrow allowlists; we now default those sessions to
--permission-mode acceptEditsbecause prefix allowlists cannot be trusted against write forms