github/copilot-sdk · error · RuntimeError

str(e)

Error message

str(e)

What it means

When CopilotClient.start() fails, the library logs the failure, sets internal state to 'error', and re-raises as `RuntimeError(str(e)) from None` — deliberately suppressing the original traceback/exception type. Developers see only the stringified underlying error, which can make the root cause (process spawn failure, binary missing, handshake timeout, etc.) harder to trace.

Solutions

  1. Read the RuntimeError message — it contains the underlying cause string.
  2. Enable logging at WARNING/DEBUG for 'CopilotClient.start failed' (log_timing output includes exc_info).
  3. Verify the CLI binary path/availability manually before calling start().
  4. Catch RuntimeError around start() and, if needed, inspect the full chain via logs since `from None` hides the original exception.

Example fix

// before
await client.start()  # bare RuntimeError, no chained traceback
// after
try:
    await client.start()
except RuntimeError as e:
    logging.getLogger(__name__).error("Copilot startup failed: %s", e)
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

import shutil
if shutil.which(cli_path) is None:
    raise FileNotFoundError(f"Copilot CLI not found: {cli_path}")

Try / catch

try:
    await client.start()
except RuntimeError as e:
    logger.error("CopilotClient.start failed: %s", e)
    raise StartupError(str(e)) from e

Prevention

When it happens

Trigger: Any failure inside start(): the Copilot CLI subprocess fails to spawn or exits during startup, the handshake/connection to the CLI fails, or an expected startup exception type (the `except` clause above the generic handler) is raised.

Common situations: Copilot CLI binary not installed or not on PATH; wrong CLI path configured; permission denied executing the binary; CLI crashes immediately due to bad arguments or version mismatch.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/54a1e161900f668d. Report an issue: GitHub.

Appendix: source

Thrown at python/copilot/client.py:2011

            self._state = "connected"
            log_timing(
                logger,
                logging.DEBUG,
                "CopilotClient.start complete",
                start_time,
            )
        except ProcessExitedError as e:
            # Process exited with error - reraise as RuntimeError with stderr
            self._state = "error"
            log_timing(
                logger,
                logging.WARNING,
                "CopilotClient.start failed",
                start_time,
                exc_info=True,
            )
            raise RuntimeError(str(e)) from None
        except Exception as e:
            self._state = "error"
            log_timing(
                logger,
                logging.WARNING,
                "CopilotClient.start failed",
                start_time,
                exc_info=True,
            )
            # Check if process exited and capture any remaining stderr
            process = self._cli_process if self._cli_process is not None else self._process
            if process and hasattr(process, "poll"):
                if isinstance(e, BrokenPipeError) and process.poll() is None:
                    try:
                        await asyncio.to_thread(process.wait, timeout=1.0)
                    except subprocess.TimeoutExpired:
                        pass
                return_code = process.poll()

View on GitHub (pinned to cd8cf15dc3)