PermissionRequest HTTP hook fails closed when endpoint is unreachable, contradicting documented non-blocking behavior

Resolved 💬 5 comments Opened Apr 10, 2026 by rullerzhou-afk Closed May 12, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code (2.1.100)

What's Wrong?

When a PermissionRequest HTTP hook is registered but the endpoint is unreachable (e.g. ECONNREFUSED because nothing is listening on the port), Claude Code silently denies the tool call with this message:

The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file).

The user did not reject anything. No permission prompt is shown. No chat fall-through. The tool is silently denied because the hook server is offline.

This contradicts what the official documentation explicitly promises about HTTP hook failures.

What the documentation says

From https://code.claude.com/docs/en/hooks (HTTP hook fields):

Error handling differs from command hooks: non-2xx responses, connection failures, and timeouts all produce non-blocking errors that allow execution to continue. To block a tool call or deny a permission, return a 2xx response with a JSON body containing decision: "block" or a hookSpecificOutput with permissionDecision: "deny".

And specifically in the HTTP response handling section:

Connection failure or timeout: non-blocking error, execution continues

What Should Happen?

Per the docs, when the HTTP hook endpoint is unreachable, Claude Code should treat the failure as non-blocking and continue execution — i.e. fall through to whatever the default permission flow is (built-in chat prompt, applied permissions.allow rules, etc.). It should not silently reject the tool call as if the user had rejected it.

Steps to Reproduce

  1. Pick a high port that nothing is listening on. Verify with netstat -ano | findstr 65530 (Windows) or lsof -i :65530 (macOS/Linux) — should be empty.
  1. Add the following to ~/.claude/settings.json (back up your existing config first):
{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "http",
            "url": "http://127.0.0.1:65530/permission",
            "timeout": 600
          }
        ]
      }
    ]
  }
}
  1. Open a new Claude Code session in any directory.
  1. Ask Claude to do something that needs permission, e.g. "Edit ./test.txt and write 'hello' to it".
  1. Observe: Claude reports "The user doesn't want to proceed with this tool use" immediately, with no permission UI shown.

Note on PermissionRequest matcher coverage

Per the docs, PermissionRequest matches on tool name with the same values as PreToolUse:

Matches on tool name, same values as PreToolUse.

This means a single misconfigured matcher: "" PermissionRequest HTTP hook with a dead endpoint blocks all permission-gated tools (Edit, Write, Bash, mcp__*, ...), not just Bash. This significantly amplifies the impact of the fail-closed behavior.

Is this a regression?

Unclear, and I cannot pin down the exact version that introduced it. What I can say:

  • The hook works correctly when the endpoint is reachable
  • I verified the documented "non-blocking on connection failure" behavior is in the public hooks docs as of today
  • Whether this is (a) a regression in failure handling or (b) a regression in matcher coverage (Edit/Write being newly routed through PermissionRequest), or (c) both — I would need a binary search across 2.1.x versions to be sure

Claude Code Version

2.1.100

Operating System

Windows 11 Pro for Workstations 10.0.26200

Terminal/Shell

Git Bash

View original on GitHub ↗

This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗