Skip to content

Subprocess spawns visible console window on Windows when host app has no console #606

@sorryhyun

Description

@sorryhyun

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions