PermissionRequest hook decision rendered 20+ times in UI for a single tool invocation

Resolved 💬 3 comments Opened Apr 30, 2026 by Suor Closed May 12, 2026

Summary

When a PermissionRequest hook approves a tool call, the UI prints the
Allowed by PermissionRequest hook line 20+ times under the same tool block,
even though the hook script executes only once.

Environment

  • Claude Code: 2.1.123
  • OS: Linux 6.18 (Manjaro)
  • Settings: defaultMode: "auto", skipAutoPermissionPrompt: true (but not auto in this session)

Symptom

For a single Read (or other) tool call, the UI shows:

● Read(.../some/file.js · lines 285-364)
  ⎿  Read 80 lines
  ⎿  Allowed by PermissionRequest hook
  ⎿  Allowed by PermissionRequest hook
  ⎿  Allowed by PermissionRequest hook
  ... (×20+)

Diagnosis

Added per-invocation logging at the top of the hook script:

echo "$(date +%s.%N) pid=$$ ppid=$PPID tool=$(jq -r '.tool_name' <<<\"\$input\")" \
    >> /tmp/read_allow_invocations.log

Result: every tool invocation produces exactly one entry in the log, with
spacing matching the actual tool-call cadence (seconds apart). No bursts of 20+
entries within milliseconds. So the hook script runs once; the UI duplicates the
"Allowed by ..." line many times. This is a UI rendering / event-emission
duplication bug, not a hook execution bug.

Hook config

Standard modern format in ~/.claude/settings.json:

"hooks": {
  "PermissionRequest": [
    { "hooks": [ { "type": "command", "command": "bash ~/.claude/hooks/read_allow.sh" } ] }
  ]
}

Hook returns:

{"hookSpecificOutput": {"hookEventName": "PermissionRequest", "decision": {"behavior": "allow"}}}

Possibly related issues

  • #29212 (closed, not planned) — about hook firing on every permission check.

This report is specifically about the UI duplication on the approve path,
with the underlying hook execution confirmed to be single-shot.

View original on GitHub ↗

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