[BUG] 2.1.300 regression (Windows): headless claude -p refuses every Write on an exFAT volume as a sensitive file (worked in 2.1.296)
Summary
Since 2.1.300, headless claude -p runs refuse every Write/Edit to files on an exFAT volume on Windows with:
Claude requested permissions to edit <path> which is a sensitive file.
The same command on 2.1.296 writes the file. This silently breaks unattended pipelines (scheduled tasks) whose working folder is on an exFAT drive. Nothing in configuration can work around it.
Same root cause as #98756 (that report is about interactive desktop sessions on 2.1.284). This report adds the headless regression and the version where our pipeline broke.
Environment
- Windows 11 Home 10.0.26200
- Claude Code CLI (npm,
@anthropic-ai/claude-code), native build@anthropic-ai/claude-code-win32-x64 - Last working: 2.1.296. Broken: 2.1.300 (build 2026-10-14, auto-updated 2026-10-15)
- Project and working folder on
Z:: internal SSD, exFAT, reported as a Fixed drive
Steps to reproduce
- Put a folder on an exFAT volume, e.g.
Z:\work, andcdinto it. - Run:
````
echo Write the text hello to the file Z:\work\x.txt using the Write tool. | claude -p --model haiku --tools Write --allowedTools Write --output-format json
- 2.1.296: the file is written. 2.1.300:
"result": "Claude requested permissions to edit Z:\work\x.txt which is a sensitive file."and apermission_denialsentry.
Measured on one machine, same command
| Target | File system | 2.1.296 | 2.1.300 |
|---|---|---|---|
| Z:\...\results\x.json | exFAT | written | refused |
| Z:\... (project root) | exFAT | - | refused |
| C:\Users\...\Temp\x.txt | NTFS | - | written |
| D:\test\x.txt | NTFS | - | written |
Things that do NOT help on 2.1.300
- Accepting the workspace trust dialog (
hasTrustDialogAccepted: true) permissions.allow:Write(/library/**),Edit(/library/**)in the project.claude/settings.json--allowedTools Write--permission-mode acceptEdits
Cause (from the 2.1.300 bundle and a Node test)
The path-safety walker calls readlink on each directory of the path to tell a plain directory from a junction. Only EINVAL counts as "plain directory"; ENOENT counts as "absent"; any other code is an error and makes the path suspect. The sensitive-file check then returns true for every file under it.
On NTFS, readlink on a plain directory fails with EINVAL. On exFAT (no reparse points) it fails with EISDIR:
const fs = require('fs');
for (const p of ['C:\\Users\\victo', 'D:\\OBS_OUTPUTS', 'Z:\\Ai_tools_work']) {
try { fs.readlinkSync(p); } catch (e) { console.log(p, e.code); }
}
// C:\Users\victo EINVAL
// D:\OBS_OUTPUTS EINVAL
// Z:\Ai_tools_work EISDIR
Suggested fix
When the entry is known to be a directory (from lstat), treat EISDIR from readlink the same as EINVAL: a plain directory.
Workaround in use
The CLI is pinned to 2.1.296 with DISABLE_AUTOUPDATER=1. A daily check will install the first release that saves on exFAT again.