Edit tool false-positive JSON validation failure on .claude/settings.json hook commands

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 17, 2026

Description

Editing .claude/settings.json via the Edit tool repeatedly fails with a false-positive JSON validation error when the edit touches a hook command string containing a single-quoted regex with . and $ metacharacters (e.g. '.ts$'). The error is:

Claude Code settings.json validation failed after edit:
Invalid JSON: JSON Parse error: Unterminated string

The resulting file (or the file prior to the failed edit) is valid JSON in every case I checked — verified independently with jq -e '<path>' .claude/settings.json, which succeeded both before and after. The Edit tool's own post-edit validator appears to trip during its diff/patch-application step rather than on the final serialized content, since the same string, written directly via a node -e "JSON.parse(...); ...; JSON.stringify(...)" + file write, passes validation immediately with no changes to the string's content.

Repro

  1. Have a .claude/settings.json with an existing hooks.PostToolUse array.
  2. Use Edit to add a new hook entry whose command value is:

``
if git diff --name-only | grep -q '.ts$'; then npx tsc --noEmit; fi 2>/dev/null || true
``

  1. The edit fails with "Unterminated string", and is rolled back (file left unchanged — no corruption).
  2. Retrying the identical Edit call fails again the same way.
  3. Writing the exact same target content via a Node script (JSON.parse the file, mutate the command field, JSON.stringify and write back) succeeds and passes the post-edit validator with no errors.

Impact

  • Each failed attempt re-sends the full embedded JSON schema for .claude/settings.json (very large) back to the model as part of the tool-error payload, burning a significant number of tokens per retry.
  • It also produces a confusing user-facing experience: repeated identical-looking failures on a file that is, in fact, valid JSON.

Workaround

Skip the Edit tool for .claude/settings.json hook edits involving shell metacharacters/regex in command strings; instead read the file, mutate it via a node -e script using JSON.parse/JSON.stringify, then verify with jq -e '<path>' .claude/settings.json.

Environment

  • Claude Code CLI on Windows 11, Git Bash tool shell.
  • Occurred while adding a PostToolUse hook with matcher Edit|Write to run tsc --noEmit conditionally based on git diff --name-only | grep -q '.ts$'.

View original on GitHub ↗