claude-code - 💡(How to fix) Fix [BUG] Hook PermissionRequest contract: setMode:'bypassPermissions' silently dropped in 2.1.110+, breaking a documented extension path [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#49525Fetched 2026-04-17 08:38:38
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×5cross-referenced ×1

The PermissionRequest hook response schema documents setMode with "bypassPermissions" as a supported value. Since Claude Code 2.1.110, a hook response that returns behavior: "allow" combined with updatedPermissions: [{type: "setMode", mode: "bypassPermissions", ...}] has its setMode operation silently dropped: the tool call is allowed, but the session permission mode never changes and the hook receives no error feedback.

This breaks a documented hook contract that every third-party hook-based integration is built against.

Error Message

The PermissionRequest hook response schema documents setMode with "bypassPermissions" as a supported value. Since Claude Code 2.1.110, a hook response that returns behavior: "allow" combined with updatedPermissions: [{type: "setMode", mode: "bypassPermissions", ...}] has its setMode operation silently dropped: the tool call is allowed, but the session permission mode never changes and the hook receives no error feedback. Hooks are positioned as Claude Code's sanctioned extension mechanism. The contract between Anthropic and the ecosystem is: if the docs list an operation and value as supported, a hook returning it will either succeed or surface an error. Silent drops violate both halves of that contract:

  1. Return a structured error in the hook response channel, or Actual: step 4 prompts again. Status line still shows default. No error appears anywhere; the hook's JSON response was well-formed and accepted at the behavior: allow level.

Root Cause

Expected: step 4 runs without a prompt because the session was switched to bypassPermissions in step 3. Status line shows bypassPermissions after step 3.

Code Example

#!/bin/bash
cat <<'EOF'
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedPermissions": [
        {"type": "setMode", "mode": "bypassPermissions", "destination": "session"}
      ]
    }
  }
}
EOF

---

{
  "hooks": {
    "PermissionRequest": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "/tmp/bypass-test.sh" }] }
    ]
  }
}
RAW_BUFFERClick to expand / collapse

Summary

The PermissionRequest hook response schema documents setMode with "bypassPermissions" as a supported value. Since Claude Code 2.1.110, a hook response that returns behavior: "allow" combined with updatedPermissions: [{type: "setMode", mode: "bypassPermissions", ...}] has its setMode operation silently dropped: the tool call is allowed, but the session permission mode never changes and the hook receives no error feedback.

This breaks a documented hook contract that every third-party hook-based integration is built against.

Why this matters for the hook platform

Hooks are positioned as Claude Code's sanctioned extension mechanism. The contract between Anthropic and the ecosystem is: if the docs list an operation and value as supported, a hook returning it will either succeed or surface an error. Silent drops violate both halves of that contract:

  • Extensions relying on the documented API have no way to detect rejection and fall back gracefully
  • The docs continue to advertise bypassPermissions as a valid setMode value (https://code.claude.com/docs/en/hooks), creating a documentation / runtime mismatch
  • Only this one value is affected — setMode: "acceptEdits", "default", "plan", "dontAsk" all continue to work as documented in the same payload structure, so the mechanism itself is live

If bypassPermissions is intentionally policy-restricted in 2.1.110+ (per that release's changelog entry about disableBypassPermissionsMode), the hook API should:

  1. Return a structured error in the hook response channel, or
  2. Document the restriction in the PermissionRequest reference, or
  3. Both

Silent drop with no feedback is the worst of the three choices for an extension surface.

Distinct from #36168

#36168 tracks the VS Code extension UI dropdown bypass toggle. This report is specifically about the hook response protocol path, which is a different code path with different consumers (any hook-based integration, not just the VS Code extension).

Minimal reproduction

The repro uses only documented APIs — a shell script hook and a settings.json entry. No third-party products involved.

  1. Create /tmp/bypass-test.sh, make executable:
#!/bin/bash
cat <<'EOF'
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedPermissions": [
        {"type": "setMode", "mode": "bypassPermissions", "destination": "session"}
      ]
    }
  }
}
EOF
  1. Add to ~/.claude/settings.json:
{
  "hooks": {
    "PermissionRequest": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "/tmp/bypass-test.sh" }] }
    ]
  }
}
  1. Launch claude, ask it to run touch /tmp/test-$(date +%s).txt. The prompt dismisses, the command runs.
  2. Ask for a second write: touch /tmp/test2-$(date +%s).txt.

Expected: step 4 runs without a prompt because the session was switched to bypassPermissions in step 3. Status line shows bypassPermissions after step 3.

Actual: step 4 prompts again. Status line still shows default. No error appears anywhere; the hook's JSON response was well-formed and accepted at the behavior: allow level.

Control: substituting acceptEdits works

Changing the mode value in the same hook payload from "bypassPermissions" to "acceptEdits" produces the documented behavior: status line switches, subsequent file edits and filesystem commands run without prompting. This rules out a systemic setMode-channel failure and narrows the defect to one specific value.

Version matrix

VersionHook setMode:bypassPermissionsHook setMode:acceptEdits
2.1.110Silently droppedWorks
2.1.111Silently droppedWorks
2.1.112Silently droppedWorks

Environment

  • Claude Code 2.1.112 (darwin-arm64, npm-installed)
  • macOS 25.3.0
  • Anthropic API provider (not Bedrock/Vertex/Foundry)
  • No disableBypassPermissionsMode set in user, project, or managed/remote settings — verified via grep across all settings file locations
  • No MDM managed-settings.json on disk

Suggested resolution

Either restore the pre-2.1.110 behavior of honoring setMode: "bypassPermissions" when no policy gates are set, OR surface structured rejection in the hook response channel so integrations can detect it and fall back. Silent drops should not be an outcome of a hook API call that the docs advertise as supported.

Related: #48865 (docs caveat on the same path, filed from the docs side).

extent analysis

TL;DR

The issue can be resolved by either restoring the pre-2.1.110 behavior of honoring setMode: "bypassPermissions" or by surfacing a structured rejection in the hook response channel.

Guidance

  • Verify that the disableBypassPermissionsMode setting is not enabled in any settings file, as this could be causing the silent drop of the setMode: "bypassPermissions" operation.
  • Test the hook with a different mode value, such as "acceptEdits", to confirm that the issue is specific to the "bypassPermissions" value.
  • Consider adding error handling to the hook to detect and handle cases where the setMode operation is silently dropped.
  • Review the documentation for the PermissionRequest hook to ensure that it accurately reflects the current behavior and any restrictions on the setMode operation.

Example

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedPermissions": [
        {"type": "setMode", "mode": "acceptEdits", "destination": "session"}
      ]
    }
  }
}

This example shows a hook response with a different mode value, which can be used to test whether the issue is specific to the "bypassPermissions" value.

Notes

The issue appears to be specific to the setMode: "bypassPermissions" operation and does not affect other mode values. The disableBypassPermissionsMode setting may be related to the issue, but it is not clear whether this setting is enabled or disabled in the affected environment.

Recommendation

Apply a workaround by adding error handling to the hook to detect and handle cases where the setMode operation is silently dropped. This will allow integrations to fall back gracefully and avoid unexpected behavior.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING