[BUG] Headless 'claude -p' fires TCC permission dialogs from startup probes unrelated to operational scope - request --no-startup-probe flag
Summary
Headless claude -p invocations from launchd-fired pollers on macOS trigger interactive TCC permission dialogs at the foreground UI for filesystem scopes the operation does not need. Once a previously-granted scope is revoked, the next launchd fire re-prompts. Asking for a startup-probe-disable flag (env var or CLI flag) so headless invocations can skip TCC-protected probes that are unrelated to the requested operation.
Context
Two long-running launchd jobs on a Mac mini invoke Claude Code in headless mode:
- An "exec poller" that polls Notion every 60 minutes and claims work via
claude -p "$(cat exec-poller.txt)" --model claude-sonnet-4-6. - A "closeout poller" that polls GitHub every 30 minutes and reconciles merged PRs via
claude -p "$(cat closeout-poller.txt)" --model claude-haiku-4-5.
Both are scoped to specific MCP tools (Notion, GitHub), and both operationally need only:
~/.claude/,~/.claude.json~/Library/Application Support/Claude/,~/Library/Caches/Claude/,~/Library/Logs/~/.npm/(for npx-resolved MCP servers)~/Projects/(for project repos under poller automation)~/Obsidian Vault/(for memory writes)- Network (Notion API, GitHub API, Anthropic API)
What we observed
Each claude -p startup probes filesystem locations beyond that operational scope. Captured via sudo fs_usage -w -f filesys against both pollers. Per-fire counts of probes into TCC-protected scopes:
| Scope | Exec hits | Closeout hits |
|---|---:|---:|
| ~/Desktop/ | 60 | 45 |
| ~/Documents/ | 134 | 54 |
| ~/Downloads/ | 44 | 33 |
| ~/Movies/ | 12 | 9 |
| ~/Music/ | 12 | 9 |
| ~/Pictures/ | 12 | 9 |
| ~/Public/ | 44 | 33 |
None of these are operationally needed by either poller.
What goes wrong in production
When the user has previously revoked one of these scopes from Claude Code's TCC entries (during a security audit, for example), macOS surfaces an interactive permission dialog the next time the poller fires. The dialog appears at the foreground UI even though the poller is running in the background under launchd. The user sees an apparently-spontaneous "Claude would like to access files in your Downloads folder" dialog at random times of day, with no active foreground Claude Code session.
Three Claude identities probe independently in this environment:
- The path-based binary at
/opt/homebrew/Caskroom/claude-code/<version>/claude. - The CLI bundle ID
com.anthropic.claude-code. - The desktop app bundle ID
com.anthropic.claudefordesktop.
Granting the scope to one identity does not stop the others from re-prompting.
What we'd like
A way to tell claude -p to skip startup probes that aren't required by the requested operation. Two shapes that would work:
- A CLI flag, e.g.
--no-startup-probeor--minimal-fs-scope, that opts out of the speculative probes. - An environment variable, e.g.
CLAUDE_SKIP_STARTUP_PROBE=1, with the same effect. This would be the easier integration point for a launchdrun-poller.shwrapper.
Either form would let pollers run with a deterministic, narrow filesystem footprint, removing the random-dialog UX problem and tightening the security posture.
Workaround in place today
Documented the minimal grant set the pollers actually need. Deny all dialogs that fall outside it. Works but reproduces every time TCC state changes. The product-side fix would be more robust.
Reproduction steps
- Configure a launchd plist that fires
claude -p "<some prompt>" --model claude-sonnet-4-6on a schedule (e.g. hourly). - From System Settings -> Privacy & Security -> Files and Folders, revoke a scope from Claude Code that the prompt does not need (e.g. Downloads).
- Wait for the next launchd fire.
- Observe the permission dialog appear at the foreground UI despite no interactive Claude Code session being active.
Minimal repro: claude -p "echo hello" against a launchd-fired plist with one scope revoked is sufficient.
Versions
- macOS: 26.4
- Claude Code: 2.1.118 (also reproduces in 2.1.119)
- Hardware: Apple Silicon Mac mini
Additional info
Happy to provide the full fs_usage traces if useful (around 130k lines per fire, captured at filesys depth, around 30 MB raw).
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗