Headless (-p) sessions launched via Windows Task Scheduler deny already-allowlisted tool calls (Bash/PowerShell/Write/Edit); identical manual invocation never fails

Status Open
Reported on v2.1.278
Maintainer reply None cached
Activity 3 comments · opened Oct 5, 2026

Environment: Windows 11 Home, Claude Code 2.1.278, launched via powershell.exe -File run_loop_batch_headless.ps1 as a Windows Task Scheduler task (Principal LogonType=InteractiveToken, explicit WorkingDirectory set in the task action).

Command:

claude -p "/loop-batch" --append-system-prompt <text> --allowedTools "Bash PowerShell Read Write Edit Glob Grep Skill WebSearch WebFetch mcp__playwright mcp__playwright-2 mcp__playwright-3 PushNotification" --output-format json

Symptom: Every tool call (Bash/PowerShell/Write/Edit) is denied as "requires approval," even though those tool names are explicitly included in --allowedTools and also covered by project-level permissions/autoMode allow rules. The session exits in ~4 seconds with $0 cost, 0 tokens. Since this runs fully unattended (no one present to approve), nothing is ever accomplished.

Timeline:

  • Last successful run: 2026-09-27 22:00 (completed normally, real cost ~$40).
  • First failed run: 2026-09-28 22:00, and every scheduled run since (one nightly run + hourly daytime runs) through at least 2026-10-06 — confirmed across ~15 consecutive hourly runs on a single day (2026-10-05), all showing the identical symptom.
  • No Claude Code version change occurred in that window (last CLI update was 2026-09-22, six days before the first failure).
  • No change to the relevant local permission settings (.claude/settings.local.json autoMode allow rules) at the failure boundary — verified via git history of the config files.

Reproduction: Manually running the exact same command, same --allowedTools string, same environment variables, from an interactive session (and also via a subagent process with equivalent env vars) — zero permission denials, every time, across multiple independent attempts over several days.

Ruled out as correlating factors: Windows session lock state (reproduced the failure moments ago with the screen unlocked and the user actively present), time of day, and battery vs. AC power state — none of these correlate with the failure.

Not in use: --permission-prompts none is not currently passed.

Question: Is there a known difference in how --allowedTools permission evaluation behaves when the process tree originates from the Windows Task Scheduler service vs. an interactive shell (e.g. window station/session isolation, access token type, environment variable inheritance) that could cause already-allowlisted tools to be denied? This is blocking all unattended/scheduled automation on this machine since 2026-09-28.

View original on GitHub ↗

3 Comments

runvouch · 9 days ago

Zero tokens and a four second exit means no API call was made, so the tools weren't denied mid-session; the run died before the first turn. In our experience that's a quoting problem, not a window station one. We'd write the resolved command line to a file from inside the ps1, right before calling claude, and diff it against what you type manually. The --append-system-prompt with no visible value stands out: if that's a variable that comes up empty under the scheduler (user env vars don't always load for scheduled tasks), the flag eats --allowedTools as its text and the tool list becomes a bare positional argument. Also log (Get-Command claude).Source and claude --version from the task itself, since a scheduled PATH can resolve a different install than your shell does, and that would explain the 09-28 boundary without a visible update.

LittleMansFactory · 7 days ago

Thanks for taking a look — really appreciate the debugging ideas.

We checked each of the three hypotheses against our actual scripts, and none of them held up on our side:

  1. --append-system-prompt resolving empty: the value we pass is a literal string hardcoded in the script, not sourced from an environment variable, so it can't come up empty under the scheduler.
  2. Quoting: the helper that builds the command line implements standard Windows argument-escaping (backslash-before-quote doubling, etc.) and matches spec on review.
  3. PATH resolving a different claude install under the scheduler: all our launcher scripts call an absolute path (%APPDATA%\npm\claude.cmd) rather than relying on PATH lookup. We also checked the Scheduled Task's principal — it runs as the same interactive user, not SYSTEM or a different account.

Also, the "0 tokens / 4-second exit" signature doesn't quite match what we're seeing now: today's two failing autonomous-runner tasks ran 238s and 641s respectively before returning an error decision, still with 0 tokens recorded. That still points to something blocking tool calls locally before (or without) ever reaching the model, rather than an argument-parsing crash.

Happy to share more raw logs if useful — still stuck on root cause from our side.

LittleMansFactory · 4 days ago

Update after further investigation (2026-10-12). We split one headless task's processing into two separate claude -p sessions — a long "investigate" session that does research/planning but performs zero Write/Edit/script-execution, and a short "apply" session (fresh process, minimal prompt, started right after) whose only job is to carry out the writes the investigate session decided on. This reduced — but did not eliminate — the denial rate, which pointed us toward a few more specific observations that might help narrow this down:

  1. The same exact write (same file, same content, same launch script/args) succeeds or fails unpredictably across otherwise-identical fresh processes. On one task we retried the "apply" session (brand-new process each time, zero shared state) 5 times in a row; all 5 were denied on the very first Edit tool call with "requires approval"/permission-denied style messages. On a different task the day before, an equivalent short "apply"-style session succeeded on the first attempt. Same machine, same script, same --allowedTools/--permission-mode auto --permission-prompts none.
  2. Interactively, the same non-determinism shows up without any headless/scheduler involvement at all. In a plain interactive session (user present, no -p, no Task Scheduler), editing one of our own hook scripts under .claude/hooks/ was denied twice in a row by "the Claude Code auto mode classifier," then succeeded on the third attempt with byte-for-byte identical tool input each time.
  3. That .claude/hooks/*.sh denial looks like the same over-broad classification described in #100791 — our hook scripts are ordinary user-authored project files (not Claude Code's own binary/settings/config), but edits to them get treated as self-modification-flagged.
  4. Point 1's "same input, different outcome across fresh processes" matches what's reported in #24737 (closed as a dup of #18846) — in our case it's not just intermittent within one session, it's intermittent across completely independent processes with no shared memory, which suggests the classifier's state/signal isn't purely local to the process.

Given this looks like the same family of issue as #87809 / #97911 (auto-mode classifier denial with no escalation path in unattended runs) rather than something specific to our Task Scheduler setup, we've stopped trying to find a local fix and are instead building a workaround: retry the write-only step a few times as fresh processes (since retries do sometimes succeed), and if it still fails after that, fall back to flagging the task for a human to finish in an interactive session instead of silently losing the work. Documenting here in case the "fresh-process retry sometimes helps, sometimes doesn't" data point is useful alongside the other reports.