# A Batch-File Shim Was Truncating My Agent's Prompts on Windows (3/24 to 21/24)

> Source: <https://dev.to/chikoku_neko/a-batch-file-shim-was-truncating-my-agents-prompts-on-windows-324-to-2124-512k>
> Published: 2026-08-16 16:05:33+00:00

Environment: Windows 11 Home, Python 3.x, Claude Code installed via `npm install -g @anthropic-ai/claude-code`

. Measured 2026-08-14 to 2026-08-16.

I was comparing two agent harnesses on the same set of web tasks. The first run of harness B scored 3 out of 24. I didn't touch the model and I didn't touch the prompt wording. I changed only how the prompt reached the process, and the score went to 21/24. Here is what was actually broken.

I was running an eval to see how well current agents handle routine web work, the kind a triage process would flag as "candidate for RPA." Two harnesses, same 8 tasks:

`max_turns=40`

), 8 tasks x 1 trial.`claude -p`

) plus Playwright, same model, 8 tasks x 3 trials = 24 runs.Success in both harnesses is decided by machine verification only: submitted form payloads compared against expected JSONL, downloaded files checked for existence and size, answers matched by regex. The agent's own claim of "done" is never trusted as a success signal.

First run of harness B: 3/24. Only the first task passed. I assumed the model was the weak link and started digging into what harness B was actually sending it.

On Windows, when you drive `claude -p`

from Python via `subprocess`

and let `shutil.which`

resolve the executable, a multi-line prompt gets truncated at the first newline before the process ever sees it. Everything after line 1 is silently dropped.

Root cause: the npm global install of Claude Code puts a file named `claude.CMD`

on PATH. That's the file `shutil.which("claude")`

resolves to — not the real executable. It's a batch wrapper. Its body is:

```
"%dp0%\node_modules\@anthropic-ai\claude-code\bin\claude.exe"   %*
```

Let me separate what I measured from what I'm inferring.

What I measured: swapping only `argv[0]`

between the two paths changes the outcome. Point it at `claude.CMD`

and the tail is gone; point it at `claude.exe`

and it arrives. Prompt, flags, and environment are identical, so the difference is the shim.

What I'm inferring: `%*`

is cmd.exe's mechanism for forwarding arguments to the wrapped command, and the newline appears to be lost in that forwarding. I have not isolated which layer drops it (cmd.exe's argument expansion, the shell Python interposes when executing a `.CMD`

, or both). Either workaround below is sufficient without knowing, so I stopped there.

Minimal reproduction:

``` python
import os, subprocess

EXE = os.path.join(os.environ["APPDATA"], "npm", "node_modules",
                   "@anthropic-ai", "claude-code", "bin", "claude.exe")
CMD = os.path.join(os.environ["APPDATA"], "npm", "claude.CMD")
PROMPT = "Follow the instruction below exactly.\nOutput the string MARKER_TAIL_9137 and nothing else."

for label, argv0 in (("claude.CMD (shim)", CMD), ("claude.exe (direct)", EXE)):
    r = subprocess.run([argv0, "-p", PROMPT], capture_output=True, text=True,
                       encoding="utf-8", errors="replace", timeout=180)
    out = (r.stdout or "") + (r.stderr or "")
    print(label, "tail_received =", "MARKER_TAIL_9137" in out)
```

Measured result:

`claude.CMD (shim)`

: `tail_received = False`

. The model's reply asks for the missing instruction, having seen only line 1: "Follow the instruction below exactly."`claude.exe (direct)`

: `tail_received = True`

.There are two fixes:

`claude.exe`

directly instead of trusting whatever `shutil.which("claude")`

returns.This is hard to catch because there's no exception, no non-zero exit code, and no warning anywhere in the pipeline. cmd.exe forwards a truncated string and everything downstream treats it as a normal, well-formed prompt. The only symptom is a low success rate, which looks identical to "the model isn't capable enough for this task." I spent time suspecting the model before I suspected the wrapper.

`--model`

flag set and was running on whatever the CLI's default model was, so this result wasn't comparable to harness A, which was pinned to sonnet-5. Confounded comparison, not a usable data point.`max_turns=40`

plus a 600-second timeout; harness B had only the timeout, no turn cap. Part of the 6/8 vs 21/24 gap comes from that difference, so don't read those two numbers as a head-to-head ranking. The claim of this post is the within-harness-B change, 3/24 to 21/24.Success rate is the product of model capability and harness quality, and the two are easy to conflate when the harness fails silently. Before concluding a model can't do a task, I now check four things: is the target (URL, path) actually present in what gets sent; is the full instruction arriving intact (print the exact prompt the process received, and read it — don't assume); can the agent actually reach whatever files it's supposed to produce or read; are model and stop conditions pinned explicitly on both sides being compared.

If I'd taken that first run at face value, I would have reported that Claude Code plus Playwright can't handle routine web work. The cause had nothing to do with the model: a batch file was dropping everything after the first newline.
