Symptom
A system_prompt string of 131,072 bytes or more kills the CLI subprocess before any API request is made:
claude_agent_sdk._errors.CLIConnectionError: Failed to start Claude Code: [Errno 7] Argument list too long: '<cli path>'
131,071 bytes spawns fine. 131,072 fails. Reproduces on every CLI version tested.
Mechanism
src/claude_agent_sdk/_internal/transport/subprocess_cli.py:291 passes the prompt as a single argv element:
elif isinstance(self._options.system_prompt, str):
cmd.extend(["--system-prompt", self._options.system_prompt])
Linux caps a single argv element at MAX_ARG_STRLEN = 32 × PAGE_SIZE = 131,072 bytes. This is a per-element limit, distinct from ARG_MAX, and is not raisable via ulimit.
Reproduced directly, independent of the SDK:
import subprocess, os
os.sysconf('SC_PAGE_SIZE') # 4096
subprocess.run(['/bin/true', 'x'*131071]) # OK
subprocess.run(['/bin/true', 'x'*131072]) # OSError errno 7, Argument list too long
The failure is in execve, so it precedes anything the CLI or the API would do with the prompt.
There is a working escape hatch
system_prompt={"type": "file", "path": "/path/to/prompt.txt"}
which routes to --system-prompt-file (subprocess_cli.py:295). Verified with a 200 KB prompt and a ~1 MiB prompt.
This shipped for #267 ("Allow passing system prompts as files", closed). That issue reported the same Argument list too long traceback; the feature landed and closed it.
So what's left
The escape hatch exists but nothing points you at it. A caller who passes a long str still gets a raw OSError wrapped in CLIConnectionError, naming the CLI path and nothing else — no mention of system_prompt, of a size limit, or of the file form that would fix it. Diagnosing it requires knowing about MAX_ARG_STRLEN.
This is arguably a docs fix plus a clearer error, not a code fix. Options, cheapest first:
- Actionable error. In
_build_command, check len(system_prompt.encode()) against the threshold and raise something that names system_prompt, the measured size, the limit, and {"type": "file", "path": ...}.
- Auto-spill. Above the threshold, write the prompt to a temp file and pass
--system-prompt-file transparently. Silently correct, but adds temp-file lifetime management.
- Docs. Note the limit and the file form wherever
system_prompt is documented.
(1) + (3) seems right. (2) is defensible but hides a real platform constraint.
The exact threshold is platform-dependent (32 * os.sysconf('SC_PAGE_SIZE')); hardcoding 131072 is wrong on a non-4 KiB-page system, and the limit does not exist at all on Windows.
Separately: the SDK inherits os.environ minus one var
subprocess_cli.py:491:
inherited_env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"}
Only CLAUDECODE is filtered. So an SDK process launched from inside a coordinator-mode CLI silently inherits CLAUDE_CODE_COORDINATOR_MODE=1 (a real CLI env var — it gates isCoordinatorMode()), which shrinks the injected agent/skill/MCP block and changes caching behavior.
This is surprising: the SDK is a separate agent, and nothing about the parent's coordinator mode should follow it. Worth either documenting or adding to the filter alongside CLAUDECODE (see #573, which is why CLAUDECODE is filtered in the first place).
Severity
Low-to-moderate. There is a working workaround, and it is already the documented-by-existence API. The cost is diagnosis time: the error names neither the cause nor the cure.
Not verified
- The 131,071/131,072 boundary I re-measured myself, on Linux,
PAGE_SIZE 4096, against /bin/true. The CLIConnectionError traceback and the 200 KB / 1 MiB --system-prompt-file successes are from the originating investigation; I did not re-run them.
- The "~19×" shrink figure originally reported for the coordinator-mode block is not re-verified and is omitted above. What I did confirm: the SDK inherits everything but
CLAUDECODE (subprocess_cli.py:491), and CLAUDE_CODE_COORDINATOR_MODE is read by the CLI (src/coordinator/coordinatorMode.ts:48 in claude-cli-internal).
- Not tested on macOS or Windows.
Related
Symptom
A
system_promptstring of 131,072 bytes or more kills the CLI subprocess before any API request is made:131,071 bytes spawns fine. 131,072 fails. Reproduces on every CLI version tested.
Mechanism
src/claude_agent_sdk/_internal/transport/subprocess_cli.py:291passes the prompt as a single argv element:Linux caps a single argv element at
MAX_ARG_STRLEN=32 × PAGE_SIZE= 131,072 bytes. This is a per-element limit, distinct fromARG_MAX, and is not raisable viaulimit.Reproduced directly, independent of the SDK:
The failure is in
execve, so it precedes anything the CLI or the API would do with the prompt.There is a working escape hatch
which routes to
--system-prompt-file(subprocess_cli.py:295). Verified with a 200 KB prompt and a ~1 MiB prompt.This shipped for #267 ("Allow passing system prompts as files", closed). That issue reported the same
Argument list too longtraceback; the feature landed and closed it.So what's left
The escape hatch exists but nothing points you at it. A caller who passes a long
strstill gets a rawOSErrorwrapped inCLIConnectionError, naming the CLI path and nothing else — no mention ofsystem_prompt, of a size limit, or of the file form that would fix it. Diagnosing it requires knowing aboutMAX_ARG_STRLEN.This is arguably a docs fix plus a clearer error, not a code fix. Options, cheapest first:
_build_command, checklen(system_prompt.encode())against the threshold and raise something that namessystem_prompt, the measured size, the limit, and{"type": "file", "path": ...}.--system-prompt-filetransparently. Silently correct, but adds temp-file lifetime management.system_promptis documented.(1) + (3) seems right. (2) is defensible but hides a real platform constraint.
The exact threshold is platform-dependent (
32 * os.sysconf('SC_PAGE_SIZE')); hardcoding 131072 is wrong on a non-4 KiB-page system, and the limit does not exist at all on Windows.Separately: the SDK inherits
os.environminus one varsubprocess_cli.py:491:Only
CLAUDECODEis filtered. So an SDK process launched from inside a coordinator-mode CLI silently inheritsCLAUDE_CODE_COORDINATOR_MODE=1(a real CLI env var — it gatesisCoordinatorMode()), which shrinks the injected agent/skill/MCP block and changes caching behavior.This is surprising: the SDK is a separate agent, and nothing about the parent's coordinator mode should follow it. Worth either documenting or adding to the filter alongside
CLAUDECODE(see #573, which is whyCLAUDECODEis filtered in the first place).Severity
Low-to-moderate. There is a working workaround, and it is already the documented-by-existence API. The cost is diagnosis time: the error names neither the cause nor the cure.
Not verified
PAGE_SIZE4096, against/bin/true. TheCLIConnectionErrortraceback and the 200 KB / 1 MiB--system-prompt-filesuccesses are from the originating investigation; I did not re-run them.CLAUDECODE(subprocess_cli.py:491), andCLAUDE_CODE_COORDINATOR_MODEis read by the CLI (src/coordinator/coordinatorMode.ts:48inclaude-cli-internal).Related
--system-prompt-file. This issue covers what that left behind: the opaque error and the missing docs.CLAUDECODEis filtered from the inherited env.