[BUG] PreToolUse/Edit hook not triggered when old_string doesn't match file content

Resolved 💬 2 comments Opened Apr 10, 2026 by ZhouZijie77 Closed May 22, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Summary

The PreToolUse hook with Edit matcher is not triggered when the old_string parameter doesn't exist in the target file. Claude Code appears to validate old_string against file content before invoking the hook, preventing the hook from correcting mismatched parameters (such as indentation differences).

Description

When configuring a PreToolUse hook for the Edit tool, the hook should be invoked before the tool executes, giving it the opportunity to inspect and modify the tool inputs. This is essential for third-party hooks like claude-tab-fix which correct indentation mismatches between old_string (generated by the model) and the actual file content.

However, our testing reveals that:

  1. When old_string exactly matches file content → hook is triggered correctly
  2. When old_string doesn't match file content → hook is NOT triggered at all

Instead, Claude Code directly returns an error: "String to replace not found in file", bypassing the PreToolUse hook entirely.

What Should Happen?

The PreToolUse hook should be invoked before any validation of old_string. The hook should have the opportunity to:

  • Detect the mismatch (e.g., file uses tabs, old_string uses spaces)
  • Correct the parameters
  • Return modified old_string and new_string for retry (exit code 2 with corrected strings on stderr)

This is consistent with the documented hook output format where exit code 2 surfaces stderr as feedback, allowing the model to retry with corrected inputs.

Error Messages/Logs

Steps to Reproduce

  1. Create a tab-indented file (e.g., main.go using tabs for indentation)
  2. Install claude-tab-fix and configure a PreToolUse/Edit hook in .claude/settings.json:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit",
        "hooks": [{ "type": "command", "command": "claude-tab-fix" }]
      }
    ]
  }
}
  1. Ask Claude to edit the file in a way that the model generates old_string with space indentation (e.g., 8 spaces instead of tabs)
  2. Observe: The error "String to replace not found" is returned immediately
  3. Check hook logs: No entry for the Edit tool call at all

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.97

Platform

Other

Operating System

Other Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

_No response_

View original on GitHub ↗

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