PowerShell tool: non-overridable "Remove-Item on system path" guard over-blocks legitimate commands (AST target mis-attribution)

Status Open
Reported on v2.1.198
Maintainer reply None cached
Activity 4 comments · opened Jul 2, 2026

Summary

The PowerShell tool blocks legitimate commands with:

Remove-Item on system path '<x>' is blocked. This path is protected from removal.

...when the command contains a Remove-Item and an unrelated token that the
static analyzer mis-attributes as the delete target. The extraction appears to pull
a token from elsewhere in the command line rather than from Remove-Item's own
arguments.

Confirmed mis-attributed shapes

  1. A later -replace '\s','' operand → target parsed as ''\s',''
  2. A co-occurring REST-URL path segment → target parsed as '/promote'
  3. A Join-Path-derived variable pointing at a dangling junction → target parsed as '/'

In each case the actual Remove-Item target is a legitimate, non-system path; the
blocked token comes from an unrelated part of the command.

Impact

The deny is classifierApprovable: false, so it is non-overridable — no
permissions.allow rule and no CLAUDE_CODE_DISABLE_* environment variable bypasses
it. This blocks otherwise-valid diagnostic and file-management commands.

Requested fix

  • Scope the Remove-Item target extraction to that command element's own arguments

(terminate at ; / |), so tokens from later commands or unrelated operands are
not captured; and/or

  • Make the guard classifierApprovable so a reviewed command can proceed.

Workaround

Delete via .NET ([System.IO.File]::Delete / [System.IO.Directory]::Delete) or the
Bash rm tool — the guard keys on the literal cmdlet name remove-item.

Environment

  • Claude Code 2.1.198
  • Windows, PowerShell tool

View original on GitHub ↗

3 Comments

itoxdev · 27 days ago

Adding another mis-attribution vector for this guard: text inside a PowerShell # comment is picked up as the delete target.

Repro

A multi-statement command where a comment line contains a slash-leading token (/init), followed by a scoped Remove-Item on a variable path:

$proj = Join-Path $pad '20260803_SetupTest3'
[System.IO.File]::WriteAllText($claudePath, "...", $enc)
# /init が CLAUDE.md を上書きした状況を再現
Remove-Item -LiteralPath $proj -Recurse -Force -Confirm:$false

Result (blocked before execution):

Remove-Item on system path '/init' is blocked. This path is protected from removal.

/init appears only in the comment (it refers to a slash command, not a filesystem path). The actual Remove-Item target is -LiteralPath $proj, a scratchpad subfolder.

Expected

Comment text (like here-string bodies in #73882 / #81378 and quoted tokens in #69461) should not be scanned as command arguments when attributing targets to Remove-Item.

Environment

  • Claude Code VSCode extension on Windows 11 Pro (10.0.26200), PowerShell tool (pwsh 7)
  • Model: claude-fable-5

Workaround

Split destructive commands into their own minimal tool call and avoid slash-leading words in comments within the same command.

🤖 Generated with Claude Code

dataxplore-ops · 16 days ago

Adding a fourth mis-attributed shape, plus a note that the drive-root rule from #78513 still fires after that issue was closed as completed.

Shape 4: target captured from a preceding statement, not an operand

The three shapes in the issue body are all operands or URL segments inside the same command element. This one crosses a statement boundary — the reported target comes from an earlier ;-separated statement:

Set-Location 'C:\my project -folder'; Remove-Item -LiteralPath 'child-dir' -Force

Blocked with:

Remove-Item on system path ''C:\my' is blocked. This path is protected from removal.

Remove-Item's actual target is the relative name child-dir. The reported target C:\my appears nowhere in the command as a path — it is the first whitespace-delimited fragment of Set-Location's quoted argument.

The same thing happens with no Remove-Item in the command at all, via rmdir through the call operator:

$p = 'C:\my project -folder\child'; & cmd /c rmdir "$p"

→ also blocked, also citing 'C:\my.

The doubled quote is a useful tell

The message renders ''C:\my' — two opening quotes. The captured token has kept the opening ' of the original quoted argument, which is what you would expect from splitting raw command text on whitespace rather than quote-aware or AST-based extraction. Consequence: any directory whose name contains a space truncates at its first space, and the fragment left over is what gets matched against the protected-path list.

The drive-root rule from #78513 still fires

#78513 ("blocks Remove-Item for any direct child of a drive root") was closed as completed on 2026-07-30. As of 2026-08-14 this still reproduces — but it is reached through the truncation above rather than through a genuine drive-root argument.

C:\my project -folder is not a drive-root child. The truncated C:\my is. So a fix that validates real arguments against the drive-root rule will not catch a fragment that only becomes a drive-root child after truncation. It may be worth checking whether the two share a code path — fixing the extraction (as this issue already proposes) would close this route into the drive-root rule as a side effect.

Impact

Confirms the classifierApprovable: false note in the body: no permissions.allow rule clears it, and the block is independent of the OS's own permissions — the command was a legitimate cleanup of an empty, already-deregistered git worktree directory.

The [System.IO.Directory]::Delete($path, $false) workaround already listed here does work; it carries no removal keyword and passes cleanly.

Environment

  • Claude Code 2.1.196
  • PowerShell 7.6.4 (Core), via the PowerShell tool
  • Windows 11 Pro 10.0.28000
  • Reproduces with only a directory whose name contains a space, located directly under a drive root
dataxplore-ops · 16 days ago

Correcting an implication in my comment above.

I wrote that the #78513 drive-root block "is reached through the truncation above rather than through a genuine drive-root argument," and suggested that fixing the extraction here would close that route as a side effect. That reads as though the drive-root rule itself is behaving and only extraction needs work. It understates the problem.

I have since tested a path with no spaces in it at allD:\zz-claude-guard-probe-nonexistent, nonexistent, direct child of a drive root — and it is still refused:

Remove-Item on system path ''D:\zz-claude-guard-probe-nonexistent'' is blocked. This path is protected from removal.

No truncation is involved, so the two defects are independent, and fixing the target extraction described in this issue will not fix #78513. Full repro and detail: https://github.com/anthropics/claude-code/issues/78513#issuecomment-5291688505

The rest of my earlier comment stands — shape 4 (target captured from a preceding ;-separated statement) and the doubled-quote tell are unaffected by this correction. If anything the quoting detail sharpens: the no-spaces case renders doubled quotes at both ends (''D:\zz-...''), while the spaced case shows a doubled quote only at the front (''D:\some') because the truncation discards the trailing one.

Showing cached comments. Read the full discussion on GitHub ↗