contenox

Why does Claude Code or Codex keep asking for permission?

Updated

SAFETY AND CONTROL Keeps asking for permission

A coding agent keeps asking for permission when the action it wants isn’t covered by an allow rule: a command your rule doesn’t match exactly, a chain of commands where one part isn’t allowed, a file outside the project, or network access the sandbox blocks. The fix is a rule for the specific safe action, not the flag that turns all prompts off.

Most “it keeps asking” reports come down to five causes. They hit every tool, in slightly different ways.

The five causes

CauseWhat you seeFix
Rule doesn’t matchYou allowed npm test, it asks for npm test -- --watchAllow the family: npm test *
Chained commandnpm install && npm test asks although both are allowedEvery part must be allowed; allow each part
Outside the projectReads or writes in another folder ask every timeAdd the folder as a working directory
Sandbox boundaryCodex asks for anything that needs the networkTurn on network for the sandbox, or allow the command
”Allow” didn’t stickYou clicked allow, and it asks again tomorrowThe approval was for this session only; write a rule

Where the rules live, and what “don’t ask again” really saves:

ToolRules file”Don’t ask again” saves to
Claude Code.claude/settings.json (project), ~/.claude/settings.json (you).claude/settings.local.json, for commands and web fetches; edit approvals last only for the session
Codex~/.codex/rules/default.rules, ~/.codex/config.toml~/.codex/rules/default.rules; restart Codex to load it
OpenCodeopencode.json (project), ~/.config/opencode/opencode.jsonNothing: “always” lasts for the session
Copilot CLIStartup flags; saved approvals~/.copilot/permissions-config.json, per repository
VS Code (Copilot)settings.json”Always Allow” is saved; “in this Session” is not
CursorSettings → Agents → Approvals & Execution, or .cursor/permissions.jsonThe allowlist

Claude Code

In Manual mode, Claude Code reads files in the project without asking and runs a fixed set of read-only commands (ls, cat, grep, find, diff, read-only git) without asking. Everything else asks: other commands, edits, web fetches.

Since v2.1.283 the built-in default is auto mode, where a classifier model approves routine actions. If you see ⏸ manual mode on in the status bar, you or your settings chose Manual.

The causes that catch people:

  • Exact match. Bash(npm test) matches only npm test. Write Bash(npm test *). And mind the space: Bash(ls*) also matches lsof.
  • Chains. With &&, ||, ;, | or a newline, every part must be allowed. A command that ends in && can’t be parsed and always asks.
  • Wrappers. npx, docker exec and devbox run aren’t stripped before matching, so Bash(eslint *) doesn’t cover npx eslint.
  • Read-only commands that still ask: an unquoted glob on a command that can write (find, sed, git), cd combined with git, or a redirect to a path outside the project or starting with ~.
  • Other folders. Start with claude --add-dir ../shared, run /add-dir, or add the folder to permissions.additionalDirectories.
  • A stricter rule elsewhere. An ask rule in the project or managed settings beats your own allow rule. Allow rules in a committed .claude/settings.json only apply after you accept the workspace trust dialog.

A rule set that removes most routine prompts:

{
  "permissions": {
    "allow": [
      "Bash(npm test *)",
      "Bash(npm run build)",
      "Bash(npm run lint *)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Bash(git push --force *)"
    ]
  }
}

Run /permissions to see every rule and which file it came from.

On Windows, “Yes, don’t ask again” saves the rule next to .claude/settings.json in the folder you started Claude in, not at the repository root. Start Claude Code from a subfolder and the rule seems to vanish next time. Also on Windows: paths on a network share (\\server\share) always ask, whatever your rules say; map the share to a drive letter and add it with --add-dir.

Codex

Codex works differently. Instead of a list of rules, it has a sandbox and an approval policy. With the usual setup, workspace-write plus on-request, Codex runs commands freely inside the sandbox and asks only when something needs to leave it.

So when Codex keeps asking, it’s almost always the sandbox:

  • Network. workspace-write has no network. npm install, pip install and git fetch fail in the sandbox, and Codex asks to rerun them outside. Turn network on in ~/.codex/config.toml:

    sandbox_mode = "workspace-write"
    
    [sandbox_workspace_write]
    network_access = true
  • Other folders. Add them to writable_roots in the same section, or pass --add-dir ../shared.

  • Protected paths. .git, .codex, .agents and .aws stay read-only even inside the workspace. Anything that writes to .git, such as a commit, needs approval.

  • Untrusted folders. Codex can start in read-only until you trust the folder, and folders without version control default to read-only.

For single commands, Codex has rules, written in ~/.codex/rules/default.rules:

prefix_rule(pattern = ["npm", "run", ["build", "test", "lint"]], decision = "allow")
prefix_rule(pattern = ["git", "push"], decision = "prompt")

The pattern is an exact prefix of the arguments. When several rules match, the strictest wins. Test a rule before you rely on it:

codex execpolicy check --pretty --rules ~/.codex/rules/default.rules -- npm run test

Run /status to see your approval policy and writable folders, and /debug-config to see whether a managed config overrides yours.

Several open GitHub issues report Codex asking although Full Access is on, mostly in the desktop app, in older or branched chats, and on Windows with “blocked by policy” errors. Update first: releases from late October 2026 fix permissions getting lost when you resume or fork a chat. If it still asks, it’s a bug, not your config.

On Windows, a lot depends on which sandbox Codex uses. The recommended one, MXC, needs Windows 11 24H2 or 25H2 with a recent update and no admin setup. The older elevated sandbox needs an admin setup step; a declined UAC prompt during that step is a common reason commands keep failing. Test a command in the sandbox with codex sandbox windows <command>.

OpenCode

OpenCode rarely asks at all. By default, it runs commands and edits files without asking. It asks only for files outside the project, for .env files, and when it repeats the same tool call three times in a row.

If it keeps asking, your config or a plugin set ask. Find the rule under permission in opencode.json or ~/.config/opencode/opencode.json. Remember that the last matching rule wins:

{
  "permission": {
    "bash": {
      "*": "ask",
      "npm test *": "allow",
      "git status *": "allow"
    }
  }
}

Here "*" comes first, and the specific allows override it. In the other order, "*": "ask" would win every time.

Clicking always in the prompt only lasts until you close OpenCode. Write the rule into the config to keep it.

GitHub Copilot

Copilot CLI asks before every tool that changes or runs something. A plain Yes is one-time. Choose Yes, and approve TOOL for the rest of the running session, or allow commands at startup:

copilot --allow-tool='shell(npm test)' --allow-tool='shell(git:*)' --deny-tool='shell(git push)'

Deny rules win over allow rules. Two more reasons it asks: you didn’t pick Yes, and remember this folder at the trust prompt, so it asks again on every start, and folders outside the trusted one need /add-dir. Flags last only for the session, so put your usual set in a shell alias.

VS Code agent mode decides per subcommand with chat.tools.terminal.autoApprove:

{
  "chat.tools.terminal.autoApprove": {
    "npm test": true,
    "/^git (status|diff|log)\\b/": true,
    "rm": false
  }
}

The false trap: false doesn’t block a command, it means “always ask”. And a chain is approved only if every part matches true, so one unknown command in a pipe brings the prompt back.

Cursor

Cursor’s run mode is under Settings → Cursor Settings → Agents → Approvals & Execution. If Cursor asks before every command, you’re probably in Allowlist with an empty list. That’s what users of the old Ask Every Time mode were moved to. Add your safe commands, or switch to Auto-review, the default for new users, where a model reviews commands that aren’t on the list.

If .cursor/permissions.json contains "terminalAllowlist": [], the allowlist is empty, and your list in Settings is ignored. Remove the key or fill it.

Some prompts stay on purpose: File-Deletion Protection, External-File Protection and Browser Protection ask even in auto modes, and risky recursive deletes always ask.

What should keep asking?

Make the routine work quiet, not everything. Keep these as prompts in every tool:

  • git push, and anything that rewrites history
  • Deleting files, and sudo
  • Installing packages, which runs install scripts from the internet
  • Network calls you didn’t expect
  • Anything touching .env, keys, or your shell profile

If you answer five prompts a day and read all five, the setup works. If you click “yes” fifty times without reading, you don’t have more safety, only more clicks. That’s the moment people reach for YOLO mode.

Contenox

Contenox comes with rules for this split. The default permission set reads freely and runs builds, tests, linters and read-only git commands without asking. Edits ask; rm, mv, sudo and redirects always ask; SSH keys and cloud credentials are blocked. For your own machine there’s a set that allows everything not named, and still asks before risky commands and history changes. Each rule is visible under Permissions in the sidebar. See built-in tools and permissions.

Related

Esc to close