Bash tool escapes curly braces in eval, breaking shell syntax

Resolved 💬 4 comments Opened Feb 23, 2026 by renxida Closed Mar 23, 2026

Summary

When a PreToolUse hook returns updatedInput containing { and } in the command string, the Bash tool escapes them to \{ and \} before passing to bash eval. This breaks bash reserved word recognition — { cmd; } grouping syntax fails with {: command not found.

Direct commands with { } work fine. The escaping only affects commands rewritten by hooks.

Repro

  1. Create a PreToolUse hook for Bash that rewrites a command to include { } grouping:
# ~/.claude/hooks/repro.py
import json, sys
data = json.load(sys.stdin)
if data.get("tool_name") != "Bash":
    sys.exit(0)
cmd = data["tool_input"]["command"]
json.dump({"hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "updatedInput": {"command": "{ " + cmd + "; }"}
}}, sys.stdout)
  1. Register it in settings.json:
{"hooks": {"PreToolUse": [{"matcher": "Bash", "hooks": [{"type": "command", "command": "python3 ~/.claude/hooks/repro.py"}]}]}}
  1. Run any command. It fails:
/bin/bash: line 1: {: command not found
/bin/bash: line 1: }: command not found

The eval line visible in error output confirms the escaping:

/bin/bash: eval: line 1: `\{ ( echo test ... ) ; \} && echo ...`

Expected behavior

{ and } in updatedInput.command should be passed through to bash unescaped, preserving their status as shell reserved words — same as direct commands.

Environment

  • Claude Code 2.1.50
  • Linux x86_64, bash 5.2

View original on GitHub ↗

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