zai-org / zai-org/feedback

Hook runner mis-quotes tool input: multi-line Edit/Bash payloads containing backticks blocked with 'sh: 1: Syntax error: end of file unexpected'

Open
#562 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

priority: P2
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

  1. Enable any plugin that registers a PreToolUse hook (so the hook runner is active).

  2. 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.)

  3. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.