Summary
On Windows, orchestrated worker spawns fail with spawn ENAMETOOLONG when the composed agent system prompt is large. The system prompt is passed as a command-line argument (--system-prompt <text> in agent-host.ts), and Windows caps a process command line at ~32,767 characters (CreateProcessW lpCommandLine). The base task-worker.md template alone is ~27 KB, leaving only a few KB of headroom before any project-specific .pi/agents/task-worker.md override — plus the segment prompt and the other args (--model, --tools, etc.) — pushes the total over the limit and the worker process never starts.
Observed in a real project (taskplane v0.30.4, pi 0.80.10, Windows 11):
base task-worker.md: 27,191 bytes
project override: 9,611 bytes
segment prompt: 1,936 bytes
─────────────
composed: ~38,738 bytes > 32,767 → spawn ENAMETOOLONG
The batch fails at wave 1 in ~2s with:
[orch] lane-1/TP-XXX: Runtime V2 execution error: spawn ENAMETOOLONG
[orch] ... task TP-XXX spawn_failure — operator action required, NOT auto-retrying
Impact
- Any Windows project with a non-trivial
.pi/agents/task-worker.md override cannot run orchestrated batches. The base worker prompt (~27 KB) leaves only ~5 KB of headroom, which is easily consumed by legitimate project-specific guidance.
- The failure is opaque unless you know
ENAMETOOLONG = command-line-length limit. Operators reasonably suspect pi-resolution or config issues first.
- Reviewer/merger prompts are smaller (bases ~11 KB / ~7 KB) so they usually survive, which makes the failure look worker-specific and confusing.
- The deprecation warning in the logs (
Passing args to a child process with shell option true) suggests shell: true is in play, which concatenates all args into one command string and makes the limit easier to hit.
Root cause
agent-host.ts:
if (opts.systemPrompt) piArgs.push("--system-prompt", opts.systemPrompt);
The entire composed system prompt (base template + local override + segment overlay) is passed inline on the command line. On Windows this is bounded by the ~32 KB CreateProcessW limit. (The task PROMPT/context is correctly sent via stdin — only the system prompt uses the size-limited cmdline path.)
Proposed fix
Pass the system prompt via a file instead of an inline arg. pi already supports file contents:
--append-system-prompt <text or file> Append text or file contents to the system prompt (can be used multiple times)
Options (in rough order of preference):
- Write the composed system prompt to a temp file and pass it by path. Add/confirm a pi
--system-prompt-file <path> flag (or reuse --append-system-prompt <file> with an empty base) and have agent-host.ts write the composed prompt to e.g. .pi/runtime/<batch>/<lane>-system-prompt.md, then pass the path. Removes the size ceiling entirely.
- Pipe the system prompt via stdin alongside the existing prompt message (a
{ type: "system_prompt", ... } control message), if pi supports it.
- Interim mitigation: detect when the composed prompt (plus estimated arg overhead) approaches the Windows limit and fail fast with an actionable error ("system prompt too large for Windows cmdline — reduce
.pi/agents/<role>.md or upgrade to file-based prompt passing") instead of the opaque ENAMETOOLONG.
Fix #1 is the durable one — it lets projects ship rich per-role overrides on Windows without hitting an invisible ceiling.
Workaround (for affected users today)
Slim .pi/agents/task-worker.md so the composed prompt (base + override + segment + args) stays under ~32 KB. In practice the override must be ≲ 3–4 KB on top of the current base. Move detailed project guidance into docs referenced by taskRunner.standards.docs / the task's "Context to Read First" (the worker reads those at runtime via stdin instructions), keeping the system-prompt override to a thin pointer.
Environment
- OS: Windows 11
- taskplane: v0.30.4
- pi: 0.80.10 (
@earendil-works/pi-coding-agent)
- Backend: Runtime V2 (subprocess spawn)
Acceptance criteria
Summary
On Windows, orchestrated worker spawns fail with
spawn ENAMETOOLONGwhen the composed agent system prompt is large. The system prompt is passed as a command-line argument (--system-prompt <text>inagent-host.ts), and Windows caps a process command line at ~32,767 characters (CreateProcessWlpCommandLine). The basetask-worker.mdtemplate alone is ~27 KB, leaving only a few KB of headroom before any project-specific.pi/agents/task-worker.mdoverride — plus the segment prompt and the other args (--model,--tools, etc.) — pushes the total over the limit and the worker process never starts.Observed in a real project (taskplane v0.30.4, pi 0.80.10, Windows 11):
The batch fails at wave 1 in ~2s with:
Impact
.pi/agents/task-worker.mdoverride cannot run orchestrated batches. The base worker prompt (~27 KB) leaves only ~5 KB of headroom, which is easily consumed by legitimate project-specific guidance.ENAMETOOLONG= command-line-length limit. Operators reasonably suspect pi-resolution or config issues first.Passing args to a child process with shell option true) suggestsshell: trueis in play, which concatenates all args into one command string and makes the limit easier to hit.Root cause
agent-host.ts:The entire composed system prompt (base template + local override + segment overlay) is passed inline on the command line. On Windows this is bounded by the ~32 KB
CreateProcessWlimit. (The task PROMPT/context is correctly sent via stdin — only the system prompt uses the size-limited cmdline path.)Proposed fix
Pass the system prompt via a file instead of an inline arg. pi already supports file contents:
Options (in rough order of preference):
--system-prompt-file <path>flag (or reuse--append-system-prompt <file>with an empty base) and haveagent-host.tswrite the composed prompt to e.g..pi/runtime/<batch>/<lane>-system-prompt.md, then pass the path. Removes the size ceiling entirely.{ type: "system_prompt", ... }control message), if pi supports it..pi/agents/<role>.mdor upgrade to file-based prompt passing") instead of the opaqueENAMETOOLONG.Fix #1 is the durable one — it lets projects ship rich per-role overrides on Windows without hitting an invisible ceiling.
Workaround (for affected users today)
Slim
.pi/agents/task-worker.mdso the composed prompt (base + override + segment + args) stays under ~32 KB. In practice the override must be ≲ 3–4 KB on top of the current base. Move detailed project guidance into docs referenced bytaskRunner.standards.docs/ the task's "Context to Read First" (the worker reads those at runtime via stdin instructions), keeping the system-prompt override to a thin pointer.Environment
@earendil-works/pi-coding-agent)Acceptance criteria
.pi/agents/task-worker.mdoverride runs orchestrated batches on Windows withoutENAMETOOLONG.ENAMETOOLONG.