claude-code - 💡(How to fix) Fix Hook `if` field is silently ignored for Edit/Write tools (works for Bash) — v2.1.100 [1 comments, 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#46100Fetched 2026-04-11 06:28:57
View on GitHub
Comments
1
Participants
1
Timeline
7
Reactions
0
Participants
Timeline (top)
labeled ×4closed ×1commented ×1cross-referenced ×1

The hook if field works for Bash tool patterns (e.g. Bash(git *)) but is silently ignored for Edit and Write tools. Any if value on a PostToolUse hook targeting Edit/Write causes the entire hook entry to be skipped — the hook never spawns, regardless of the value.

Related: #39612 (docs gap for if), #41376 (closed as dup of #36389), #36389, #36332.

Root Cause

The hook if field works for Bash tool patterns (e.g. Bash(git *)) but is silently ignored for Edit and Write tools. Any if value on a PostToolUse hook targeting Edit/Write causes the entire hook entry to be skipped — the hook never spawns, regardless of the value.

Related: #39612 (docs gap for if), #41376 (closed as dup of #36389), #36389, #36332.

Fix Action

Fix / Workaround

The official workaround (filter inside the hook script by parsing tool_input.file_path from stdin) still works, but forces a Node process spawn on every Write/Edit even for unrelated files. For a codebase with frequent Edit/Write traffic this is measurable overhead, and defeats the purpose of having a declarative if filter at the configuration level.

Code Example

{
  "matcher": "Write|Edit",
  "hooks": [{
    "type": "command",
    "command": "node /path/to/script.js",
    "if": "Edit(*.resx)|Write(*.resx)"
  }]
}

---

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "command",
          "if": "Bash(git *)",
          "command": "bash -c 'echo HOOK_FIRED_BASH >> /tmp/hook.log'"
        }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{
          "type": "command",
          "if": "Edit(**)|Write(**)",
          "command": "bash -c 'echo HOOK_FIRED_EDIT >> /tmp/hook.log'"
        }]
      }
    ]
  }
}
RAW_BUFFERClick to expand / collapse

Version

Claude Code 2.1.100 (Windows 11, Node.js hook scripts).

Summary

The hook if field works for Bash tool patterns (e.g. Bash(git *)) but is silently ignored for Edit and Write tools. Any if value on a PostToolUse hook targeting Edit/Write causes the entire hook entry to be skipped — the hook never spawns, regardless of the value.

Related: #39612 (docs gap for if), #41376 (closed as dup of #36389), #36389, #36332.

Expected behavior

Per the undocumented if field (introduced v2.1.85 per #39612) using permission rule syntax, the following should filter a hook to only fire on matching file paths:

{
  "matcher": "Write|Edit",
  "hooks": [{
    "type": "command",
    "command": "node /path/to/script.js",
    "if": "Edit(*.resx)|Write(*.resx)"
  }]
}

The hook should fire on Edit/Write of .resx files and be skipped for other extensions.

Actual behavior

The hook never fires as long as the if field is present on an Edit/Write hook entry, regardless of the pattern value. Removing the if field makes the hook fire correctly on every Edit/Write.

Reproduction

.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "command",
          "if": "Bash(git *)",
          "command": "bash -c 'echo HOOK_FIRED_BASH >> /tmp/hook.log'"
        }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{
          "type": "command",
          "if": "Edit(**)|Write(**)",
          "command": "bash -c 'echo HOOK_FIRED_EDIT >> /tmp/hook.log'"
        }]
      }
    ]
  }
}

Steps:

  1. Ask Claude to run git status via Bash → HOOK_FIRED_BASH is appended ✅
  2. Ask Claude to run echo hello via Bash → log unchanged (correctly filtered out) ✅
  3. Ask Claude to Write any file → log unchanged ❌ (hook should have fired)
  4. Ask Claude to Edit any file → log unchanged ❌ (hook should have fired)
  5. Remove if from the PostToolUse entry → Write/Edit now fires the hook ✅

Patterns tested (all fail for Edit/Write)

if valueFires?
`EditWrite`
`Edit(*)Write(*)`
`Edit(**)Write(**)`
`Edit(.*)Write(.*)`
`Edit(*.resx)Write(*.resx)`
`Edit(.*Resources.resx)Write(.*Resources.resx)`
`Edit(**/Resources.resx)Write(**/Resources.resx)`
Edit(/.claude/hooks/test/Resources.resx)
Edit(.claude/hooks/test/Resources.resx)
Edit(/**/Resources.resx)
Write(src/.*) (copying #41376 comment syntax)
(removed entirely)

Equivalent Bash(git *) vs Bash(echo *) filtering in the same settings.json works correctly as expected — confirming the if field infrastructure is wired up but the Edit/Write code path is missing or broken.

Impact

The official workaround (filter inside the hook script by parsing tool_input.file_path from stdin) still works, but forces a Node process spawn on every Write/Edit even for unrelated files. For a codebase with frequent Edit/Write traffic this is measurable overhead, and defeats the purpose of having a declarative if filter at the configuration level.

Request

  1. Make Edit/Write if patterns consume tool_input.file_path consistently with how Bash consumes tool_input.command.
  2. Document the if field officially (currently absent from code.claude.com/docs/en/hooks; see #39612).
  3. Clarify the exact syntax — gitignore spec, permission rule syntax, or regex — since #39612 mentions permission rule syntax but existing permission rule docs describe gitignore patterns for Read/Edit.

extent analysis

TL;DR

The if field in Edit and Write hooks is being silently ignored, causing the entire hook entry to be skipped, and a fix would involve modifying the hook infrastructure to correctly handle the if field for these tools.

Guidance

  • Review the hook infrastructure code to identify why the if field is being ignored for Edit and Write tools, and modify it to correctly handle the if field.
  • Verify that the if field is working as expected for Bash tools and apply similar logic to Edit and Write tools.
  • Consider adding official documentation for the if field, including its syntax and usage, to avoid similar issues in the future.
  • Test the if field with various patterns and tools to ensure it is working consistently across all use cases.

Example

No code snippet is provided as the issue is related to the internal implementation of the hook infrastructure, but an example of a correctly working if field for Bash tools is shown in the issue body:

{
  "matcher": "Bash",
  "hooks": [{
    "type": "command",
    "if": "Bash(git *)",
    "command": "bash -c 'echo HOOK_FIRED_BASH >> /tmp/hook.log'"
  }]
}

This example should be used as a reference to implement the correct handling of the if field for Edit and Write tools.

Notes

The issue is specific to the Edit and Write tools, and the if field is working correctly for Bash tools. The fix should focus on modifying the hook infrastructure to correctly handle the if field for these tools.

Recommendation

Apply a workaround by filtering inside the hook script by parsing tool_input.file_path from stdin, as mentioned in the issue body, until the if field is officially supported for Edit and Write tools. This will allow for some level of filtering, although it will incur additional overhead due to the spawn of a Node process on every Write/Edit.

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…

FAQ

Expected behavior

Per the undocumented if field (introduced v2.1.85 per #39612) using permission rule syntax, the following should filter a hook to only fire on matching file paths:

{
  "matcher": "Write|Edit",
  "hooks": [{
    "type": "command",
    "command": "node /path/to/script.js",
    "if": "Edit(*.resx)|Write(*.resx)"
  }]
}

The hook should fire on Edit/Write of .resx files and be skipped for other extensions.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING