hey, i was exploring src/claude_agent_sdk/_internal/transport/subprocess_cli.py to understand how the SDK spawns the Claude Code CLI subprocess and noticed that connect() always builds the subprocess environment by inheriting the full parent os.environ and then merging user provided ClaudeAgentOptions.env on top of it
inherited_env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"}
process_env = {
**inherited_env,
"CLAUDE_CODE_ENTRYPOINT": "sdk-py",
**self._options.env,
"CLAUDE_AGENT_SDK_VERSION": __version__,
}
at a normal usage level this makes sense because env={"MY_VAR": "value"} works as an override/addition mechanism, the confusing part is that this inheritance behaviour does not seem to be explicitly documented on ClaudeAgentOptions.env and there also does not appear to be a supported way to request a clean or isolated subprocess environment
this looks related to #573 but in this case the concern is broader as users currently get implicit full inheritance but no explicit way to choose between merge with parent env and isolated/clean subprocess env
Impact
due to this, users cannot create a deterministic or sandboxed subprocess environment and also, sensitive parent variables may be inherited unintentionally and CI/reproducibility use cases become harder
(main point: current behaviour may be intentional but the API contract is not explicit enough to make that clear)
How to reproduce
Reproduction
import os
import anyio
from claude_agent_sdk import ClaudeAgentOptions
from claude_agent_sdk._errors import CLIConnectionError
from claude_agent_sdk._internal.transport.subprocess_cli import SubprocessCLITransport
class EnvCaptured(Exception):
pass
async def fake_open_process(*args, **kwargs):
env = kwargs["env"]
print("MY_CUSTOM_VAR:", env.get("MY_CUSTOM_VAR"))
print("HOME inherited:", "HOME" in env)
print("PATH inherited:", "PATH" in env)
print("ANTHROPIC_API_KEY inherited:", "ANTHROPIC_API_KEY" in env)
raise EnvCaptured("Stopped after capturing subprocess env")
async def fake_check_claude_version():
return None
async def main():
os.environ["ANTHROPIC_API_KEY"] = "dummy-secret"
options = ClaudeAgentOptions(
env={"MY_CUSTOM_VAR": "hello"},
max_turns=1,
)
transport = SubprocessCLITransport(prompt="hello", options=options)
transport._cli_path = "dummy-claude"
transport._build_command = lambda: ["dummy-claude"]
transport._check_claude_version = fake_check_claude_version
original_open_process = anyio.open_process
anyio.open_process = fake_open_process
try:
await transport.connect()
except CLIConnectionError as e:
if "Stopped after capturing subprocess env" in str(e):
print("Captured env successfully; reproduction complete.")
else:
raise
finally:
anyio.open_process = original_open_process
anyio.run(main)
Output:

was the current inheritance-only behaviour intentional or is the lack of an isolated environment mode an oversight? happy to make a pr if this direction sounds useful
hey, i was exploring
src/claude_agent_sdk/_internal/transport/subprocess_cli.pyto understand how the SDK spawns the Claude Code CLI subprocess and noticed thatconnect()always builds the subprocess environment by inheriting the full parentos.environand then merging user providedClaudeAgentOptions.envon top of itat a normal usage level this makes sense because
env={"MY_VAR": "value"}works as an override/addition mechanism, the confusing part is that this inheritance behaviour does not seem to be explicitly documented onClaudeAgentOptions.envand there also does not appear to be a supported way to request a clean or isolated subprocess environmentthis looks related to #573 but in this case the concern is broader as users currently get implicit full inheritance but no explicit way to choose between merge with parent env and isolated/clean subprocess env
Impact
due to this, users cannot create a deterministic or sandboxed subprocess environment and also, sensitive parent variables may be inherited unintentionally and CI/reproducibility use cases become harder
(main point: current behaviour may be intentional but the API contract is not explicit enough to make that clear)
How to reproduce
Reproduction
Output:

was the current inheritance-only behaviour intentional or is the lack of an isolated environment mode an oversight? happy to make a pr if this direction sounds useful