Hooks on Windows: the PowerShell wrapper reads the payload as ANSI, so non-ASCII text reaches the hook double-encoded

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

On Windows, Cursor runs a hook through a PowerShell wrapper that reads the JSON payload file with Get-Content -LiteralPath <payload> -Raw and no -Encoding argument. On Windows PowerShell 5.1 a file without a byte-order mark is read as the ANSI code page, so every non-ASCII character in the payload arrives at the hook process double-encoded: “é” (UTF-8 c3 a9) reaches stdin as c3 83 c2 a9. Cursor’s own hook log records the same text intact. The hook has no way to tell this happened.

The wrapper, as Cursor logs it:

$OutputEncoding = [System.Text.Encoding]::UTF8; Get-Content -LiteralPath ‘\cursor-hooks-\payload.json’ -Raw | & { $input | & ‘’ }

Fix is one token on your side: Get-Content … -Raw -Encoding UTF8, or write the payload with a BOM, or pass it on stdin directly.

Steps to Reproduce

  1. Windows 11, Windows PowerShell 5.1 (no pwsh 7 installed).
  2. Install a preToolUse (or beforeShellExecution) hook in ~/.cursor/hooks.json that runs an executable which dumps its stdin bytes to a file.
  3. In the Agent, send a prompt containing non-ASCII text, e.g. café 東京 naïve, so that the model’s reasoning or a tool input quotes it.
  4. Compare the bytes the hook received with the same text in Cursor’s hook log (%APPDATA%\Cursor\logs<date>\window*\output_\cursor.hooks..log).

Expected Behavior

The hook receives the payload bytes Cursor logged: “café” as 63 61 66 c3 a9.

Operating System

Windows 10/11

Version Information

Cursor IDE 3.21.9 (9998796a6096ce83d83a9332bfe7473b985db750), vscode 1.128.0
Also reproduced on 3.21.16
Windows 11 Home 10.0.26200.9457
Windows PowerShell 5.1.26100.9444 (Desktop edition)

For AI issues: which model did you use?

grok-4.6 (agent mode). The model is not the cause; the wrapper is.

For AI issues: add Request ID with privacy disabled

Not available: the conversation was on a test machine and has since been cleared.
Cursor’s own hook log identifies it as workspace/conversation 96bcf583 on 2026-09-21,
10:47–10:48 UTC. The bug reproduces from the steps above without it.

Additional Information

Actual bytes received by the hook for the same sentence: 63 61 66 c3 83 c2 a9 20 c3 a6 c2 9d c2 b1 c3 a4 c2 ba c2 ac 20 6e 61 c3 83 c2 af 76 65 (29 bytes for 19 characters). Cursor’s hook log, same sentence: 63 61 66 c3 a9 20 e6 9d b1 e4 ba ac 20 6e 61 c3 af 76 65 (19 bytes). Reproduced on two sittings, 2026-09-18 and 2026-09-21. Machines with PowerShell 7 as the default would not show it, since pwsh defaults to UTF-8.

No request id is attached; this is not a model-output issue and it reproduces
deterministically from the steps.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey, thanks for the detailed report and the byte-by-byte comparison. Your diagnosis is spot on.

This is a known issue we’re tracking, and I’ve linked your report to it. You identified the cause correctly: on Windows PowerShell 5.1, the payload file is read using the ANSI code page before it ever reaches the hook via stdin.

For now, you can get clean UTF-8 in any of these ways:

  1. Install PowerShell 7 and make sure pwsh is in PATH, then fully restart Cursor. If pwsh is available, Cursor will prefer it over Windows PowerShell.
  2. Or enable Beta: Use Unicode UTF-8 for worldwide language support in Control Panel > Region > Administrative > Change system locale, then reboot. Note this affects all apps.
  3. Or in the hook, read the transcript_path file directly from the payload. It’s written as UTF-8 and doesn’t go through PowerShell.

One more thing: in PowerShell 5.1, the stdin payload can start with a UTF-8 BOM EF BB BF, so if you see it, strip it before parsing.

I can’t share an ETA yet, but I’ll post in the thread if there’s an update.

Hey @Quasar_AI, here’s the promised update.

A fix for this issue is already ready: the hook wrapper now reads the payload file as UTF-8, so non-ASCII text like café 東京 naïve should reach your hook without corruption on Windows PowerShell 5.1. It’ll be included in one of the next Cursor releases, but I can’t name the exact version yet.

Once you’ve updated, could you please run your byte comparison again and let me know if anything still looks off? Until then, the workarounds from the message above still work: pwsh 7 in PATH or reading transcript_path directly. The note about stripping the leading BOM on stdin also still applies, since this fix doesn’t affect that part.

Thanks again for the detailed byte-level breakdown. It really helped.