PricingSign in57Download

Blog / claude code

Why Claude Code asks about a command you already allowed

Bash(echo:*) runs echo $HOME but not echo $PORT, ${HOME}, $(…), a redirect to a file or a chained rm. What we tested, and the fix for each.

On this page
  1. What ran and what didn't
  2. The fixes
  3. Why not just allow everything?
  4. When the question comes while you're away
  5. Frequently asked questions
  6. Why does Claude Code ask for permission when the command is allowed?
  7. How do I let Claude Code run a command with an environment variable?
  8. Do pipes and && need separate permission in Claude Code?
  9. Why is a redirect to a file blocked in Claude Code?
  10. Try it with your team

You allowed Bash(npm:*) or Bash(echo:*), and Claude Code still stops to ask about a command that starts with npm or echo. In a claude -p run it's worse: the command is refused, and the agent says something like "echo $PORT was blocked because the tool couldn't check the variable before running it".

A prefix rule covers a plain command. Once the command line contains something Claude Code can't check in advance, such as a variable it can't vouch for, a command inside a command, or a second command you didn't allow, it asks again. We tried each shape with Claude Code 2.1.318 in claude -p, where "ask" means refused.

What ran and what didn't#

CommandAllowed withResult
echo helloBash(echo:*)ran
echo $HOMEBash(echo:*)ran
echo $PORTBash(echo:*)asked
echo $MY_TOKENBash(echo:*)asked
echo ${HOME}Bash(echo:*)asked
ls *.txtBash(ls:*)ran
ls 2>/dev/nullBash(ls:*)ran
ls > out.txtBash(ls:*)asked
cat $(ls)Bash(cat:*), Bash(ls:*)asked
`cat ls `Bash(cat:*), Bash(ls:*)asked
ls && echo doneBash(ls:*), Bash(echo:*)ran
`ls \wc -l`Bash(ls:*), Bash(wc:*)ran
ls; rm a.txtBash(ls:*)asked

The pattern:

  • Variables. $HOME passed; $PORT, $MY_TOKEN and even ${HOME} in braces did not. A variable's value isn't known until the command runs, so a rule can't promise what it expands to.
  • A command inside a command. $(…) and backticks run something first and paste in the result. Both were refused even though cat and ls were each allowed.
  • Writing a file. ls > out.txt writes a file, which is an edit, not just a command. It went through once we also ran with --permission-mode acceptEdits.
  • Chains. && and | between allowed commands ran. ls; rm a.txt didn't, because rm wasn't allowed. Every part has to be covered.

The fixes#

Read a variable without $. printenv takes the name as a plain argument:

bash
claude -p "Run: printenv PORT. Reply with only its output." --allowedTools 'Bash(printenv:*)'
3002

Allow the exact command. A rule with the whole command line, variables and all, matches that command and nothing else:

bash
claude -p "Run exactly: echo \$PORT" --allowedTools 'Bash(echo $PORT)'

That ran. It's the right tool for one command the agent needs every time, such as a build script that reads $CI.

Let it write files the normal way. If the agent is meant to change files, run it with acceptEdits (or /permissions in a session) rather than allowing shell redirects one by one. Then ls > out.txt stops asking, and so do the agent's ordinary edits.

Split a chain. Ask for the steps as separate commands in your CLAUDE.md ("run each command on its own, never chain with ;"). Each one is then checked against its own rule, which is what you wanted from the rules in the first place.

Take out the $(…). Instead of cat $(ls), have the agent run ls and then read the files it named. Two allowed steps instead of one that can't be checked.

Why not just allow everything?#

--dangerously-skip-permissions makes every prompt go away, including the ones you want, like rm in a chain. The table above is mostly Claude Code being careful in exactly the cases where a prefix rule would have let something through you didn't mean to. A variable can hold a path you didn't expect; $(…) can run anything. Fix the command shape and keep the check.

If prompts in general are slowing you down, our post on Claude Code permission prompts covers allow rules and modes, and the deny rule that didn't work covers the opposite problem.

When the question comes while you're away#

In an interactive session, every one of these is a prompt that waits for you. Joxo shows an agent's question on your phone and sends your answer back into the session, so a single echo $PORT doesn't hold up an hour of work. See approving agent actions from your phone.

Frequently asked questions#

Why does Claude Code ask for permission when the command is allowed?#

The command line contains something an allow rule can't vouch for: a variable such as $PORT, ${…}, $(…) or backticks, a redirect that writes a file, or a second command after ; that isn't allowed. Each needs its own approval.

How do I let Claude Code run a command with an environment variable?#

Use printenv NAME instead of echo $NAME, or allow the exact command line, for example Bash(echo $PORT). In our test the exact rule ran and Bash(echo:*) did not.

Do pipes and && need separate permission in Claude Code?#

Each part needs to be allowed. ls | wc -l and ls && echo done ran with ls, wc and echo allowed. ls; rm a.txt was refused because rm wasn't allowed.

Why is a redirect to a file blocked in Claude Code?#

Writing to a file counts as an edit. With --permission-mode acceptEdits, ls > out.txt ran under a Bash(ls:*) rule; without it, it was refused.