Why does Claude Code keep asking for permission?

Answer

If you have not configured anything, check the status bar first. Enterprise, API-key and -p sessions start in Manual mode, which asks by design before edits and before commands outside a small read-only set. Pro, Max and Team sessions start in auto mode. If you did pick a mode and it still asks, seven things prompt regardless, among them explicit ask rules, protected paths such as .git and .claude, and anything outside the mode's scope.

What it does

If you have not configured anything, this is probably Manual mode working as designed: it asks before every file edit, every shell command outside a small read-only set, and most network calls. The prompt is the review step, not a misconfiguration.

Whether you start there depends on your plan. Pro, Max and Team terminal sessions start in auto mode from v2.1.228; -p runs, Enterprise plans, API keys and the cloud providers start in Manual. The status bar tells you which: a grey ⏸ manual mode on against ⏵⏵ auto mode on.

If the status bar shows a different mode than the one you configured, the prompts are a symptom of something else: the mode was set but not applied.

That read-only set is worth knowing, because it explains why some commands never ask: ls, cat, echo, pwd, head, tail, grep, find, wc, which, diff, stat, du, cd, and read-only forms of git. You cannot add to the set, but an ask or deny rule for one of these commands still takes effect. Rules are evaluated before the set is consulted.

Even these prompt in Manual mode in a few shapes: an unquoted glob next to a command with a flag that can write or execute, such as find, sed or git; docker pointed at another daemon; a cd into a different directory followed by git, since that can run the directory's hooks; and any command Claude Code cannot fully parse, including anything over 10,000 characters.

The rest of this page is about the other case. You picked a mode that should have removed the prompt, and it appeared anyway. Seven documented things prompt regardless of the mode.

An explicit ask rule

A rule you wrote with type ask forces a prompt even in auto and bypassPermissions. That is its job. The one exception is dontAsk, where there is nobody to prompt, so the same rule becomes a refusal.

A connector tool your organisation set to ask

A connector is an MCP server your organisation manages centrally rather than one you added yourself; an admin can mark its tools as always-ask. These prompt even when one of your allow rules matches.

An MCP tool that requires interaction

A server can mark a tool with _meta["anthropic/requiresUserInteraction"], and it prompts in every mode. Requires Claude Code v2.1.199 or later. On an older version the same tool will not prompt, which is worth knowing before you go looking for a rule that is not there.

A write to a protected path

.git, .claude, .vscode, .idea and others, the directories holding repository state and Claude's own configuration, are never auto-approved except in bypassPermissions, which also lifts the block during planning in sessions where it is available.

A removal targeting / or your home directory

A circuit breaker against model error, and no allow rule or hook can approve it. Manual, acceptEdits and bypassPermissions ask; auto sends it to the classifier, the second model that reviews actions in your place; dontAsk denies it outright; plan asks unless the classifier is reviewing planning commands.

The list is wider than / and ~: any top-level directory, your working directory and its parents, and rm -rf "$DIR"/*, because an empty variable makes that a removal from the root. Hiding it inside $(...) does not skip the check.

A read outside the working directories

Once permissions.blockReadsOutsideWorkingDirectories is on, a file-reading command aimed outside your working directories prompts in every mode, auto and bypassPermissions included. Requires v2.1.257 or later. Auto mode sets this for you if you answer Block from now on the first time it asks about such a read.

Anything outside the mode's scope

acceptEdits covers edits and seven filesystem commands inside your working directory. A path outside that scope, or any other shell command, is not something the mode ever claimed to approve.

How to check

Read the prompt itself first. It names the tool and the target, which is usually enough to tell a protected path from an out-of-scope path from an ask rule.

Two of the seven, a protected path and a critical-path removal, are settled before your rules are consulted at all, so no allow entry will silence them.

For the rest, your rules are evaluated in a fixed order:

deny  →  ask  →  allow

The first match wins, and how specific a rule is does not change that. A broad deny beats a narrow allow; a matching ask prompts even when an allow also matches. Only when nothing matches does the mode decide, and in auto mode the classifier decides in its place.

This order has nothing to do with which file a rule was written in: rules from every scope merge into one list first, then get evaluated. Which file wins is a separate question, and it applies to the mode rather than to rules. See setting a default permission mode.

That order is the whole explanation for the most confusing case: a rule you wrote having no visible effect. Either something earlier in the chain matched first, or the call never reached the chain.

How to fix it

Match the fix to the cause, and resist escalating the mode to silence one command.

  • One command asks repeatedly — add a permissions.allow rule for that command. It fixes the specific prompt without removing every other one.
  • The prompt is a protected path — allow rules cannot help here. For a .claude/ write the prompt itself offers a per-session approval; take that instead.
  • You want fewer prompts on edits — that is acceptEdits, not a rule.
  • You want no prompts at all in a throwaway environment — that is bypassPermissions, with the caveats on that page.

Example

Rules live under permissions in a settings file: .claude/settings.json for the project, ~/.claude/settings.json for you everywhere. All three types share one shape:

{
  "permissions": {
    "allow": ["Bash(npm test:*)"],
    "ask": ["Bash(git push:*)"],
    "deny": ["Read(./.env)"]
  }
}

Each entry is Tool or Tool(specifier). The tool is the one Claude would call, such as Bash, Read, Edit or WebFetch, and the specifier narrows it. What goes in the parentheses depends on the tool: a command for Bash, a path for the file tools, domain:example.com for WebFetch.

Without a wildcard the match is exact: Bash(npm run build) covers that command and nothing else. A trailing :* is shorthand for a trailing *, so Bash(npm test:*) matches npm test with any arguments. It only works at the end. In Bash(git:* push) the colon is just a character. A bare Bash with no parentheses matches every shell command.

allow runs without asking, ask forces a prompt even in modes that would not have asked, deny refuses outright.

Now the case that catches people. A rule like this looks like it should stop the prompts for Claude's own config:

{
  "permissions": {
    "allow": ["Edit(.claude/**)"]
  }
}

It does not. The protected-path check runs before Claude Code evaluates allow rules from settings, so the per-mode outcome is unchanged: default and acceptEdits still prompt, dontAsk still denies.

What does work is the prompt itself. For a .claude/ write it offers:

Yes, and allow Claude to edit its own settings for this session

That approves later .claude/ writes for the rest of the session.

Common mistakes

Reading a prompt in auto mode as a bug. Explicit ask rules are designed to survive it.

Blaming the mode for out-of-scope paths. acceptEdits is bounded by your working directory and additionalDirectories. A prompt for a path outside them is the mode working as documented. --add-dir is the fix.