dontAsk: Write allow-list broken for all path forms on Windows native (2.1.145); PreToolUse hook confirmed workaround

Resolved 💬 2 comments Opened Jun 1, 2026 by maxbatura Closed Jul 4, 2026

Summary

--permission-mode dontAsk denies Write calls on Windows native (not WSL2) regardless of the --allowedTools Write(...) pattern used. Every path form was tested systematically at Claude Code 2.1.145 — all fail. A PreToolUse hook works correctly as a workaround.

Extends prior reports: #40076 (WSL2, ** glob, v2.1.86), #52962 (WSL2, multiple glob forms, v2.1.117). This extends both to native Windows and adds a confirmed workaround.

Environment

  • Claude Code: 2.1.145
  • OS: Windows 11 (native — not WSL2)
  • Python: 3.14.0 (spike harness only; Claude Code itself is Node)
  • Auth: Max subscription (OAuth login, no API key)

Tested allow-list forms — all DENIED

| Form | Pattern example | Outcome |
|---|---|---|
| Backslash absolute glob (W1) | Write(C:\tmp\work\**) | DENIED |
| Forward-slash exact path (W1a) | Write(C:/tmp/work/target.txt) | DENIED |
| Relative pattern (W1b) | Write(./target.txt) with CWD=workdir | DENIED |
| Forward-slash absolute glob (W1c) | Write(C:/tmp/work/**) | DENIED |

All tested via claude --permission-mode dontAsk --allowedTools "<pattern>" -p "..." at the same version on the same machine.

Root cause evidence

A PreToolUse hook records the exact tool_input the Write tool delivers:

{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "C:\Users\...\target.txt",
    "content": "..."
  }
}

The Write tool always delivers file_path with Windows backslashes. The --allowedTools matcher appears to compare against this path without normalizing separators. Forward-slash patterns (W1a, W1c) cannot match a backslash path. For the backslash pattern (W1), the ** glob may not traverse backslash separators.

Note: Bash(echo *), Bash(pytest *), and similar allow-list patterns do work under the same conditions — the Bash glob matcher operates on command strings that contain no path separators.

Workaround (confirmed working)

Configure a PreToolUse hook in .claude/settings.json instead of relying on --allowedTools for Write path filtering:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "python",
            "args": ["/path/to/write_guard.py"]
          }
        ]
      }
    ]
  }
}

The hook receives file_path on stdin and can normalize separators (replace("\\", "/")) before comparing against allowed paths. Hooks fire correctly under --permission-mode dontAsk on Windows native.

Reproduction

import subprocess, sys, tempfile, pathlib

with tempfile.TemporaryDirectory() as tmp:
    tmp = pathlib.Path(tmp)
    target = tmp / "test.txt"
    allowed = f"Write({tmp.as_posix()}/**)"  # forward-slash form

    result = subprocess.run(
        ["cmd", "/c", "claude", "--permission-mode", "dontAsk",
         "--allowedTools", allowed, "-p",
         f"Use the Write tool to create {target} containing 'hello'."],
        capture_output=True, text=True, cwd=str(tmp)
    )
    print("created:", target.exists())  # prints False — should be True

Related

  • #40076 — same glob-matching failure in WSL2 at v2.1.86 (closed/stale)
  • #52962 — same failure in WSL2 at v2.1.117, multiple glob forms (closed/duplicate)

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗