Skip to content

ZCode stop.ps1 on Windows misdecodes UTF-8 payload under GBK locale; guard skipped, blocks every turn #142

Description

@psikent

Describe the bug

The ZCode Stop hook template (internal/memory/setup/assets/zcode/stop.ps1, also embedded in the 0.2.8 binaries) reads the hook payload with a bare [Console]::In.ReadToEnd(). On Windows, powershell.exe decodes piped stdin using the system ANSI/OEM code page — CP936 (GBK) on a zh-CN locale — while the client sends the payload as UTF-8. Any multi-byte content (e.g. Chinese text in last_assistant_message) arrives as mojibake and can make ConvertFrom-Json throw; the catch then sets $payload = $null, which silently skips the stop_hook_active early-exit. Net effect: the hook emits decision: block at the end of every turn, re-prompting the memory evaluation even when it was already done.

A second, encoding-independent problem hits non-English users in the same template: the exit guard only matches English keywords (mnemon, durable memory), so a reply that already performed the evaluation in another language (e.g. Chinese 「无需写入持久记忆」) never matches and the block loop still doesn't close.

To reproduce

  1. Windows with system locale zh-CN (OEM code page 936); mnemon setup --target zcode (v0.2.8) to deploy the hooks.
  2. End a turn whose last_assistant_message contains Chinese. The hook log shows ConvertFrom-Json failing (payload null → stop_hook_active check skipped) and the hook returning decision: block unconditionally.
  3. Alternatively, reply with a completed memory evaluation phrased in Chinese — the English-only guard misses it and the hook blocks again.

Expected behavior

The hook should decode the UTF-8 payload correctly regardless of the system code page, and close the loop when stop_hook_active is set or the evaluation was already done in a non-English reply.

Environment

  • OS: Windows 11 x64 (build 26220), system locale zh-CN (OEM code page 936)
  • mnemon version: 0.2.8 (@mnemon-dev/mnemon via npm)
  • LLM CLI: ZCode (hook scripts run under Windows PowerShell 5.1)
  • Ollama: n/a

Workaround / suggested fix (verified locally)

At the top of stop.ps1, before reading stdin:

try { [Console]::InputEncoding = [Text.Encoding]::UTF8 } catch {}

Extend the guard list with non-English phrases:

$guards = @("mnemon", "durable memory", "持久记忆", "写入记忆")
foreach ($g in $guards) {
    if ($lastMessage.Contains($g)) { exit 0 }
}

And ship the .ps1 asset as UTF-8 with BOM: Windows PowerShell 5.1 treats BOM-less files as ANSI, which corrupts the non-ASCII literals in the guard list (a generate-time concern for the embedded template).

With these three changes deployed locally, the hook parses the payload and exits correctly (verified via hook logs); without them, every turn end was blocked unconditionally. The same stdin-decoding consideration applies to the other ZCode .ps1 hooks if they ever parse non-ASCII payload fields.

Additional context

  • Checked that the template is unchanged on master (internal/memory/setup/assets/zcode/stop.ps1, as of 2026-09-15) and inside the 0.2.8 win32-x64 binary; no existing issue tracks this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions