PreToolUse hook permissionDecision:"deny" not enforced in Claude Desktop/Cowork sessions (works in native CLI)

Open 💬 1 comment Opened Jul 15, 2026 by Tulalaba

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

On Claude Code v2.1.200/v2.1.209 (Windows), a PreToolUse hook configured
per the documented format (exit 0 + JSON on stdout:
hookSpecificOutput.permissionDecision:"deny") correctly blocks a Bash
command when Claude Code is launched via native CLI, but does NOT block
the same command when the session is orchestrated via Claude Desktop /
Cowork.

Tested directly in both contexts: same hook, same .claude/settings.json,
same payload structure.

  • Native CLI: command is intercepted before execution, tool result is

tagged <error>, exact permissionDecisionReason text is returned, no
git output at all (confirmed fail-closed as expected).

  • Claude Desktop / Cowork session: the same hook does not block the

command; it executes normally.

Environment variables CLAUDE_CODE_CHILD_SESSION, AI_AGENT, and
CLAUDE_CODE_ENTRYPOINT were checked and found IDENTICAL in both
contexts (CLAUDE_CODE_CHILD_SESSION=1, AI_AGENT=claude-code_2-1-200_agent,
CLAUDE_CODE_ENTRYPOINT=cli in both cases) — so these do not explain the
difference.

The discrepancy appears to originate from an orchestration layer specific
to Desktop/Cowork sessions that isn't reflected in process environment
variables, but this is not confirmed.

Happy to provide the exact hook command, full settings.json snippet, and
reproduction steps if useful.

What Should Happen?

The PreToolUse hook's permissionDecision:"deny" should block the Bash command consistently, regardless of whether the session runs via native CLI or via Claude Desktop/Cowork orchestration.

Error Messages/Logs

Steps to Reproduce

  1. Configure a PreToolUse hook on Bash in .claude/settings.json that returns {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"..."}}} for a matching command (e.g. git reset --hard without a required prefix).
  2. In a native CLI session (claude launched directly from a terminal), trigger the matching command → the hook fires, the command is blocked, tool result is tagged <error> with the exact permissionDecisionReason text.
  3. In a Claude Desktop / Cowork session, trigger the exact same command with the exact same .claude/settings.json → the hook does not block it, the command executes normally.
  4. Environment variables CLAUDE_CODE_CHILD_SESSION, AI_AGENT, CLAUDE_CODE_ENTRYPOINT were checked and found identical in both sessions — they don't explain the difference.

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.200

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

Possibly related (same general area — permissionDecision resolution being unreliable depending on execution context), though none match this exact failure mode:

  • #62437 (deny bypassed after session-level ask approval, macOS)
  • #77037 (allow never suppresses prompt, macOS/plugin)
  • #64389 (defer breaks tool call entirely, VS Code extension, macOS)

Happy to provide the exact hook command, full settings.json snippet, and reproduction steps if useful.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗