Summary
When using claude-agent-sdk from a Windows GUI application (e.g., a PyInstaller bundle with console=False), the claude.exe subprocess spawns a visible console window. There is no way to suppress this through ClaudeAgentOptions.
Environment
- SDK version: 0.1.40
- OS: Windows 11
- Python: 3.12
- Host app: PyInstaller-bundled exe with
console=False (windowed mode, no console)
Problem
SubprocessCLITransport.connect() in subprocess_cli.py (line ~369) calls:
self._process = await anyio.open_process(
cmd,
stdin=PIPE,
stdout=PIPE,
stderr=stderr_dest,
cwd=self._cwd,
env=process_env,
user=self._options.user,
)
On Windows, when the parent process has no console (GUI app / windowed exe), subprocess.Popen without creationflags=CREATE_NO_WINDOW causes the OS to allocate a new visible console window for the child process. This means every time a Claude agent is invoked, a black terminal window flashes or stays open — which is disruptive for desktop applications.
The same issue applies to _check_claude_version() (line ~592) which also calls anyio.open_process without creation flags.
anyio.open_process() already supports forwarding creationflags and startupinfo as keyword arguments to the underlying subprocess.Popen, but the SDK does not pass them.
Current workaround
Monkey-patching subprocess.Popen.__init__ to inject CREATE_NO_WINDOW:
import subprocess
CREATE_NO_WINDOW = 0x08000000
_original_popen_init = subprocess.Popen.__init__
def _patched_popen_init(self, *args, **kwargs):
if "creationflags" not in kwargs or kwargs["creationflags"] == 0:
kwargs["creationflags"] = CREATE_NO_WINDOW
_original_popen_init(self, *args, **kwargs)
subprocess.Popen.__init__ = _patched_popen_init
This works but is fragile and affects all subprocesses globally.
Proposed solution
Option A: Auto-detect (recommended)
When running on Windows without an attached console, automatically set CREATE_NO_WINDOW:
import sys
import os
# In SubprocessCLITransport.connect():
kwargs = {}
if sys.platform == "win32":
import ctypes
# If no console is attached, suppress child console windows
if not ctypes.windll.kernel32.GetConsoleWindow():
kwargs["creationflags"] = 0x08000000 # CREATE_NO_WINDOW
self._process = await anyio.open_process(
cmd,
stdin=PIPE,
stdout=PIPE,
stderr=stderr_dest,
cwd=self._cwd,
env=process_env,
user=self._options.user,
**kwargs,
)
Option B: Expose in ClaudeAgentOptions
Add a field to ClaudeAgentOptions that lets callers control subprocess window visibility:
@dataclass
class ClaudeAgentOptions:
...
# Windows-specific: creation flags for subprocess (e.g., CREATE_NO_WINDOW = 0x08000000)
subprocess_creationflags: int = 0
Then forward it in the transport:
kwargs = {}
if sys.platform == "win32" and self._options.subprocess_creationflags:
kwargs["creationflags"] = self._options.subprocess_creationflags
self._process = await anyio.open_process(cmd, ..., **kwargs)
Option C: Both
Auto-detect by default (Option A), but allow override via ClaudeAgentOptions (Option B).
Impact
This affects any Windows desktop application that embeds the Claude Agent SDK — Electron apps, PyInstaller/cx_Freeze bundles, Tauri sidecars, or any GUI host without a console. The flashing terminal window degrades user experience significantly.
Summary
When using
claude-agent-sdkfrom a Windows GUI application (e.g., a PyInstaller bundle withconsole=False), theclaude.exesubprocess spawns a visible console window. There is no way to suppress this throughClaudeAgentOptions.Environment
console=False(windowed mode, no console)Problem
SubprocessCLITransport.connect()insubprocess_cli.py(line ~369) calls:On Windows, when the parent process has no console (GUI app / windowed exe),
subprocess.Popenwithoutcreationflags=CREATE_NO_WINDOWcauses the OS to allocate a new visible console window for the child process. This means every time a Claude agent is invoked, a black terminal window flashes or stays open — which is disruptive for desktop applications.The same issue applies to
_check_claude_version()(line ~592) which also callsanyio.open_processwithout creation flags.anyio.open_process()already supports forwardingcreationflagsandstartupinfoas keyword arguments to the underlyingsubprocess.Popen, but the SDK does not pass them.Current workaround
Monkey-patching
subprocess.Popen.__init__to injectCREATE_NO_WINDOW:This works but is fragile and affects all subprocesses globally.
Proposed solution
Option A: Auto-detect (recommended)
When running on Windows without an attached console, automatically set
CREATE_NO_WINDOW:Option B: Expose in
ClaudeAgentOptionsAdd a field to
ClaudeAgentOptionsthat lets callers control subprocess window visibility:Then forward it in the transport:
Option C: Both
Auto-detect by default (Option A), but allow override via
ClaudeAgentOptions(Option B).Impact
This affects any Windows desktop application that embeds the Claude Agent SDK — Electron apps, PyInstaller/cx_Freeze bundles, Tauri sidecars, or any GUI host without a console. The flashing terminal window degrades user experience significantly.