PermissionRequest hook decision rendered 20+ times in UI for a single tool invocation
Summary
When a PermissionRequest hook approves a tool call, the UI prints theAllowed 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗