Hook runner mis-quotes tool input: multi-line Edit/Bash payloads containing backticks blocked with 'sh: 1: Syntax error: end of file unexpected'
Nobody has claimed this yet.
- Dominant language
- No language data
- Stars
- 22
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
Summary
When any plugin hook is registered (PreToolUse), an Edit/Write/Bash tool call whose payload is multi-line AND contains backtick characters (Markdown code fences) is blocked before reaching the hook or the tool. The tool result is a dash shell syntax error:
sh: 1: Syntax error: end of file unexpected
or, depending on payload content:
sh: 1: Syntax error: Unterminated quoted string
Root cause (external evidence, host is closed-source): the hook runner appears to embed the tool-call payload inside a double-quoted shell string when invoking hooks. Backticks inside double quotes trigger command substitution, so the text between the payload's code fences is parsed by dash as shell code and fails on quotes, backslashes and parentheses.
Environment
- OS: Debian 12, kernel 6.1.0-49-amd64, x86_64 (sh = dash)
- ZCode: 3.11.2 Linux desktop (/opt/ZCode/zcode)
- Model channel: Z.ai
- Active plugin hooks during repro: mimosa 1.0.3 (PreToolUse Edit|Write|Bash), planning-with-files 3.16.1 (PreToolUse Write|Edit|Bash|Read|Glob|Grep, Stop)
Steps to reproduce
-
Enable any plugin that registers a PreToolUse hook (so the hook runner is active).
-
Have the agent perform an Edit whose old_string/new_string spans several lines and contains a Markdown triple-backtick code fence plus shell-style content, e.g. a fenced block of five or more lines with line continuations and quoted arguments:
(fence: three backticks + "bash") # verify then hot-reload /usr/bin/verge-mihomo -d ~/.local/share/app \ -t -f ~/.local/share/app/clash-verge.yaml curl -s -X PUT --unix-socket /tmp/verge/verge-mihomo.sock \ -H "Content-Type: application/json" \ -d '{"path":"/abs/path/clash-verge.yaml"}' http://localhost/configs (closing fence)(Literal backtick characters are intentionally omitted from this report: any tool call payload containing them multi-line is blocked by this very bug, including the Write call that would file the report.)
-
The Edit is rejected. The file is not modified. The tool result contains only the sh syntax error above.
Expected vs actual
- Expected: hook input is delivered to hooks without shell interpretation (e.g. via stdin pipe or safely quoted argv); the Edit applies.
- Actual: the payload is parsed by dash as shell code; the syntax error aborts dispatch; the tool call is blocked.
Evidence (bisection on a scratch file, same session)
- Same 6-line payload with backslash line-continuations, single-quoted JSON and Chinese parentheses PASSES once the code fences are removed (zero backticks).
- The same payload WITH surrounding triple-backtick fences FAILS every time.
- Small 1-3 line edits pass even with backticks or quotes alone.
- Failing calls never reach the plugin hook: mimosa's own debug log (~/.zcode/mimosa-debug.log) records every received PreToolUse with outcome clear; the blocked calls produce no entry at all, so the crash happens in the runner's dispatch stage before any plugin script runs.
- All readable plugin hook scripts (mimosa .mjs launchers, planning-with-files claude-hook.sh/gate-stop.sh/check-complete.sh) were executed manually with sample stdin for every registered event: all exit 0. No user-level configuration hooks exist (no hooks key in ~/.zcode/cli/config.json, no settings.json).
- Two distinct dash error messages observed (end of file unexpected / Unterminated quoted string), varying with payload content — consistent with content-dependent shell parsing, not a fixed broken script.
Impact
Any agent turn that edits files containing fenced code blocks (documentation, Markdown manuals, README files) is silently blocked whenever the edit chunk is large enough. Agents burn turns retrying, and only accidental fragmentation of the edit lets work continue.
Workaround (confirmed)
Split large edits into pieces of a few lines each and keep triple-backtick fence lines out of old_string/new_string; edit fenced code from inside the fences. Alternatively disable all hook-contributing plugins (loses their features).
Suggested fix
Do not embed hook input in a double-quoted shell string. Pass the JSON payload via stdin (already the documented hook contract) or via an environment variable set with fork/exec semantics rather than sh -c string interpolation. If a shell wrapper is unavoidable, single-quote the payload with the standard ''' escaping so no character in it is interpreted.
Logs
Can provide ~/.zcode/v2/logs/2026-09-08.log and ~/.zcode/mimosa-debug.log excerpts on request (no hook failure is recorded in either, which is itself part of the evidence). Repro session exec logs: ~/.zcode/cli/exec/sess_a682f8f7-dbda-494f-a972-52fa241e6e47/.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The failure occurs in the closed-source hook-runner dispatch before PreToolUse reaches the mimosa or planning-with-files scripts. Start by reviewing the documented hook contract and the referenced session and plugin logs; the issue is done when a multiline payload containing backticks reaches both the hook and the tool without shell parsing errors, with a regression test covering that case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- shell
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100