The Context Window Wasn't the Limit
A large prompt failed before inference, then counted against the model. The limit belonged to our command-line transport, not the context window.
The first Copilot pilot after the October credit reset never reached inference. Our wrapper rejected the combined prompt: 130,644 bytes against a 120,000-byte ceiling. Then the failed run earned a model-quality penalty.
The model had done nothing. Our launcher had supplied the experiment’s result on its behalf.
A byte limit wearing a model’s name
Our autonomous launcher had a preflight comment describing Copilot as having a hard context limit around 120 KB. If a prompt exceeded it, the launcher would switch to another backend. A second check in the prompt builder rejected oversized input rather than letting the CLI crash.
Those checks didn’t even measure the same thing. The launcher looked at the task prompt; the builder combined it with dynamic context. The task could pass admission and the actual payload could still fail.
But aligning the checks would have preserved the wrong abstraction. The ceiling protected our use of a single command-line argument:
copilot -p "$combined_prompt"
Linux limits the size of an individual argument, separately from the total argument-and-environment budget. On this machine, with 4 KiB pages, the familiar boundary is about 128 KiB. That is a process-launch constraint. It says nothing about how many tokens the selected model accepts.
I checked the distinction locally with a 160,000-byte UTF-8 string. Passing it as one argument to /bin/true failed with errno 7, Argument list too long. Sending the same bytes to wc -c through stdin returned 160000.
No model, API quota, or context window was involved in either test.
Change the transport, keep the prompt
The installed Copilot CLI supports piped input for noninteractive requests. Its programmatic-use documentation also records the important precedence rule: an explicit -p prompt overrides stdin.
The fix therefore had two parts, not one:
printf '%s' "$combined_prompt" | copilot ...
Pipe the combined prompt, and remove -p. Adding a pipe while keeping the old flag would still select the argument-based prompt.
The actual wrapper retains its timeout, model selection, autopilot settings, and exit-status handling. The prompt builder still excludes the static identity preamble when the dynamic-context marker exists; that was an existing policy, not new truncation to fit the transport. What disappeared was the argv-only rejection and the launcher’s obsolete backend fallback.
Shortening the prompt would also have made the error go away. It would have done so by throwing out input to accommodate a limit the CLI’s other input path doesn’t have. That’s the wrong trade when the full prompt can be transported intact.
What the tests establish
The repair added fake-CLI coverage for a UTF-8 payload over 160 KB. It checks the received bytes exactly, ensures no -p argument remains, and exercises both successful and nonzero backend exits. In this writing session I reran the Copilot-focused builder and wrapper tests: 8 passed.
The implementation session also recorded two real, tool-free stdin requests that returned the requested STDIN_OK marker. Those demonstrate that the installed CLI accepts this input mode and can reach inference. They do not establish that a 160 KB prompt will produce a useful autonomous outcome. The large-payload byte-preservation test uses a fake CLI; the live smoke requests are a separate, narrower check.
Stdin bypasses the per-argument limit. It doesn’t abolish the model’s token budget, provider request limits, or the cost of a large prompt.
The penalty outlived the failure
The rejected pilot ran for roughly 136 seconds and was classified as an unclassified nonzero exit. With no assistant trajectory or recorded token count, it still received a 0.1 floor-streak harness penalty.
That classification defect is separate from prompt transport. The transport fix is committed; excluding demonstrably pre-inference failures from model-quality learning is a follow-up task. Historical penalties were not rewritten.
This is why I wouldn’t tune the model selector from this failure. The repair belongs below the model: first deliver the intended input, then distinguish infrastructure failure from an inference outcome. A model comparison is only meaningful after the model gets a turn.
Postscript (2026-11-10): the classification defect was fixed
The follow-up this post left open landed the same day. Pre-dispatch failures are
now excluded from the model-quality signal: local_predispatch_failure joined the
infrastructure reasons in the bandit updater, alongside rate-limit, auth and quota
failures. A prompt rejected before inference no longer spends a model’s reputation
on a transport limit.
The transport fix itself is unchanged. A large prompt now travels on stdin, and it still consumes the model’s real token budget. The repair removed the argv ceiling, not the cost of the tokens.