# Qwen Code permissions, approval modes and hooks reference

Qwen Code's five approval modes, settings.json keys, allow/ask/deny rules, sandbox options, 23 hook events and headless behaviour, from its own docs.

Qwen Code has five approval modes: `plan`, `default` (labelled Ask Permissions), `auto-edit`, `auto` and `yolo`. The default is `auto`, where an LLM classifier decides which actions run; you set the mode with `tools.approvalMode` in `settings.json` or `--approval-mode` on the command line. Below that sit `permissions.allow` / `ask` / `deny` rules, an optional sandbox, and a hook system with `PreToolUse` and `PostToolUse` that can allow, ask, deny or rewrite any tool call.



[Qwen Code](https://github.com/QwenLM/qwen-code) is Alibaba's open-source terminal coding agent, forked from Gemini CLI. Everything here comes from its repository docs and source on `main` as of 2026-11-07. For installing a hook that records every call, see the [Qwen Code install guide](/integrations/qwen-code); for how Qwen Code compares with other harnesses, see the [Qwen Code control model](/controls/qwen-code).

## Approval modes

The [settings schema](https://github.com/QwenLM/qwen-code/blob/main/packages/cli/src/config/settingsSchema.ts) defines `tools.approvalMode` with `default: ApprovalMode.AUTO`. The [settings reference](https://github.com/QwenLM/qwen-code/blob/main/docs/users/configuration/settings.md) describes each value:

| Mode | File edits | Shell commands |
|---|---|---|
| `plan` | Not run. "Analyze only" | Not run |
| `default` (Ask Permissions) | Prompt | Prompt |
| `auto-edit` | Auto-approved (`edit`, `write_file`, `notebook_edit`) | Prompt |
| `auto` **(default)** | Classifier decides | Classifier decides |
| `yolo` | Auto-approved | Auto-approved |

The [approval mode doc](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/approval-mode.md) notes that the mode once named Default is now called Ask Permissions; the config value `"default"` is kept for compatibility. That page is not consistent with itself about which mode Qwen Code starts in. The schema is the source of truth: `auto`.

Three ways to set it:

```bash
# Per run
qwen --approval-mode auto-edit
qwen --yolo                       # same as --approval-mode yolo; the two cannot be combined
```

```json
{
  "tools": { "approvalMode": "default" }
}
```

In a session, Shift+Tab (Tab on Windows) cycles plan → default → auto-edit → auto → yolo. `/approval-mode <mode>` sets it directly, and `/approval-mode yolo --project` or `--user` writes the choice to that scope's settings file. `/plan` enters plan mode and `/plan exit` restores the previous one.

### What auto mode does

The [auto mode doc](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/auto-mode.md) describes an LLM classifier that reviews shell commands, network calls and edits outside the workspace. It runs on your configured fast model (`/model --fast`), or the main model if none is set. Most read-only operations and in-workspace edits skip it.

- A block fails the call with `Blocked by auto mode policy: <reason>`.
- If the classifier is unreachable, times out or returns bad output, that action falls back to a manual approval prompt. After two unavailable results in a row, later calls go straight to manual approval.
- After three blocks in a row, the third falls back to the manual prompt.
- `permissions.deny` runs before the classifier. Over-broad allow rules such as `["Bash"]` are set aside while auto mode is on and restored on exit.

You can steer it under `permissions.autoMode`: `hints.allow`, `softDeny` and `hardDeny` lists (the older `hints.deny` is still read as `softDeny`), `environment` for natural-language context, and `classifyAllShell` to send every shell command through the classifier.

## Settings files and precedence

From the [settings reference](https://github.com/QwenLM/qwen-code/blob/main/docs/users/configuration/settings.md), lowest to highest priority:

1. Built-in defaults
2. System defaults file: `/etc/qwen-code/system-defaults.json` (Linux), `/Library/Application Support/QwenCode/system-defaults.json` (macOS), `C:\ProgramData\qwen-code\system-defaults.json` (Windows)
3. User settings: `~/.qwen/settings.json`
4. Project settings: `.qwen/settings.json` in the project root
5. System settings: `/etc/qwen-code/settings.json` (Linux), `/Library/Application Support/QwenCode/settings.json` (macOS), `C:\ProgramData\qwen-code\settings.json` (Windows)
6. Environment variables
7. Command-line arguments

`QWEN_CODE_SYSTEM_DEFAULTS_PATH` and `QWEN_CODE_SYSTEM_SETTINGS_PATH` override the two system paths. Because the system settings file sits above user and project files, an administrator can pin values for every user on a machine.

## Permission rules: allow, ask, deny

The `permissions` block uses the same rule grammar as Claude Code:

```json
{
  "permissions": {
    "allow": ["Bash(git status)", "Read"],
    "ask":   ["Bash(git push *)"],
    "deny":  ["Read(./.env)", "Bash(rm -rf *)", "mcp__untrusted"]
  }
}
```

Rules from the [settings reference](https://github.com/QwenLM/qwen-code/blob/main/docs/users/configuration/settings.md):

- **Priority:** `deny` > `ask` > `allow` > the approval mode. Rules are merged across user, project and system scopes.
- **Format:** `"ToolName"` or `"ToolName(specifier)"`. `Bash(git *)` matches commands starting with `git` at a word boundary, so not `gitk`.
- **Meta-categories:** `Read` covers `read_file`, `grep_search`, `glob` and `list_directory`. `Edit` covers `edit`, `write_file` and `notebook_edit`.
- **Paths:** `//` is absolute, `~/` is home, `/` is the project root, `./` or no prefix is the working directory.
- **Shell equivalents:** file and network rules are also enforced against shell commands that do the same thing, such as `cat .env`.
- **Deny removes:** a whole-tool deny takes a built-in tool out of the registry. MCP tools stay registered and are blocked at runtime.

For a single run, `--allowed-tools "Shell(git status)"` lets listed tools bypass the confirmation dialog, and `--exclude-tools shell,write,edit` removes built-ins entirely.

### Deprecated keys still read

`tools.allowed` (use `permissions.allow`) and `tools.exclude` (use `permissions.deny`) are deprecated. `tools.exclude` is not migrated automatically and is still honoured at startup. `tools.core`, an allowlist for built-in tools, is marked deprecated and due for removal. Check all three when auditing an older install.

## Folder trust

[Folder trust](https://github.com/QwenLM/qwen-code/blob/main/docs/users/configuration/trusted-folders.md) is off by default. Turn it on in user settings:

```json
{
  "security": { "folderTrust": { "enabled": true } }
}
```

Decisions are saved in `~/.qwen/trustedFolders.json`. In an untrusted folder Qwen Code does not load the project's `.qwen/settings.json` or `.env` files, and it prompts before every tool runs, "even if you have auto-acceptance enabled globally."

## Sandbox

The [sandbox doc](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/sandbox.md) lists two separate mechanisms. Both are off by default.

**Whole-CLI sandbox.** Enable it with the environment variable, the flag or the setting; the variable wins over the other two:

```bash
QWEN_SANDBOX=docker qwen          # true | false | docker | podman | sandbox-exec
qwen -s -p "run the tests"        # or --sandbox=<provider>
```

```json
{ "tools": { "sandbox": true } }
```

On macOS `true` picks Seatbelt (`sandbox-exec`) with the `permissive-open` profile. `SEATBELT_PROFILE` switches to `permissive-closed`, `permissive-proxied`, `restrictive-open`, `restrictive-closed`, `restrictive-proxied`, or a custom `.qwen/sandbox-macos-<name>.sb`. On Linux and Windows it needs Docker or Podman. The image comes from `--sandbox-image`, then `QWEN_SANDBOX_IMAGE`, then `tools.sandboxImage`, then the built-in default.

**Linux tool-execution sandbox.** `tools.executionSandbox` confines only shell and tool execution with bubblewrap, or Landlock as a fallback. It is read from user or system settings, not workspace settings:

```json
{
  "tools": {
    "executionSandbox": { "backend": "auto", "filesystem": "workspace-write", "network": "closed" }
  }
}
```

`qwen sandbox --verify` checks it. The doc lists what it leaves alone: the CLI, model transport, auth and session storage stay on the host, reads stay broad, and hooks, extensions, MCP and several other features are disabled while it is on.

## Hooks

Qwen Code has a full hook system. The [hooks doc](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/hooks.md) lists 23 events, including `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `UserPromptSubmit`, `SessionStart`, `SessionEnd`, `Stop`, `SubagentStart`, `SubagentStop`, `PreCompact`, `PermissionRequest` and `PermissionDenied`.

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "run_shell_command",
        "hooks": [{ "type": "command", "command": "/path/to/check.sh", "timeout": 10 }]
      }
    ]
  }
}
```

The contract:

- **Matcher:** a regex on the runtime tool id (`write_file`, `read_file`, `run_shell_command`). `""`, `"*"` and `".*"` match everything. Display aliases such as `Bash` and `Write` are also accepted.
- **Types:** `command`, `http` and `prompt` for users. A fourth type, `function`, is internal to skills and the SDK.
- **Timeout:** seconds, default 60 for command hooks. For compatibility, a command-hook value of 1000 or more is read as milliseconds.
- **stdin:** `session_id`, `transcript_path`, `cwd`, `hook_event_name`, `timestamp`, `permission_mode`; `PreToolUse` adds `tool_name`, `tool_input` and `tool_use_id`, and `PostToolUse` adds `tool_response`.
- **Decision:** `hookSpecificOutput.permissionDecision` of `allow`, `deny` or `ask`, with `permissionDecisionReason`. `updatedInput` replaces the whole tool input; `additionalContext` is appended to the result the model sees.
- **Exit codes:** `0` is success, `2` blocks and sends stderr to the model, anything else is a non-blocking error.
- **Off switch:** `"disableAllHooks": true` (schema default `false`, restart required). `--safe-mode` and `--bare` also turn hooks off.

Project-level hooks in `.qwen/settings.json` load only after you approve that exact configuration (stored in `~/.qwen/hookApprovals.json` and invalidated by any edit) or after folder trust is granted. Non-interactive runs never prompt for this, so unapproved project hooks stay unloaded and `qwen -p` prints a warning. User-level hooks in `~/.qwen/settings.json` have no such gate.

## Extensions

[Extensions](https://github.com/QwenLM/qwen-code/blob/main/docs/users/extension/introduction.md) are directories with a `qwen-extension.json` manifest under `~/.qwen/extensions`, installed with `qwen extensions install <source>`. They contribute MCP servers, commands, skills, subagents, context files, workflows and settings. Gemini CLI extensions and Claude Code plugins are converted on install. The introduction does not list hooks or tool exclusions as manifest fields, so for blocking a call, hooks and `permissions.deny` are the tools.

## Headless runs

`qwen -p "..."` (or piped stdin) runs non-interactively, per the [headless doc](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/headless.md). `--output-format` takes `text`, `json` or `stream-json`.

- `--yolo` in CI auto-approves every tool call, including shell, write and edit, and does not turn on a sandbox. The doc's suggested shape for an isolated runner is `qwen -p "..." --yolo --max-session-turns N --max-wall-time 10m --output-format json`, adding `--sandbox` on shared infrastructure.
- `--max-tool-calls` and `--max-wall-time` abort with exit code 55; `--max-session-turns` exits 53.
- A hook that answers `ask` in a headless run or a background subagent is treated as `deny`. Confirming an ask runs the tool once, and no approval mode, yolo included, can approve in its place.

The docs do not say what a built-in confirmation prompt does in a `-p` run, and the approval mode page contradicts the schema on the headless default. In CI, pass `--approval-mode` explicitly rather than relying on the default.

## Where the model ends

- **`auto` uses a model's judgment.** The default mode puts a model call in front of shell commands. It falls back to a prompt when it fails, but its answers can differ run to run. For a fixed boundary, use `default` or `auto-edit` and write rules.
- **`--yolo` has no limits of its own.** Only `permissions.deny` and hooks still apply, and it does not turn on a sandbox.
- **The sandboxes limit where a command runs, not which command.** The Linux execution sandbox also disables hooks.
- **No record.** Approval modes, rules and hooks decide each call but keep no log of what was allowed or refused. A `PostToolUse` hook that writes somewhere you control is the only built-in way to keep one.

ACP's Qwen Code hook adds a log of every call plus rules that run in every approval mode, yolo included. A held call is blocked, and you approve it on the console or from the email, then retry. In a headless run Qwen Code itself turns an ask into a deny. The shared hook fails open if ACP is unreachable, unless you set `ACP_FAIL_MODE=closed` or write `closed` to `~/.acp/failmode`. The [Qwen Code install guide](/integrations/qwen-code) has the steps.

## Frequently asked questions



## Where to read more

- [Qwen Code repository](https://github.com/QwenLM/qwen-code)
- [Settings reference](https://github.com/QwenLM/qwen-code/blob/main/docs/users/configuration/settings.md)
- [Approval mode](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/approval-mode.md) and [auto mode](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/auto-mode.md)
- [Hooks](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/hooks.md)
- [Sandbox](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/sandbox.md)
- [Trusted folders](https://github.com/QwenLM/qwen-code/blob/main/docs/users/configuration/trusted-folders.md)
- [Headless mode](https://github.com/QwenLM/qwen-code/blob/main/docs/users/features/headless.md)
- [Qwen Code control model](/controls/qwen-code) and [install guide](/integrations/qwen-code)
