github/copilot-sdk · warning · ExceptionGroup
errors during CopilotClient.stop()
Error message
errors during CopilotClient.stop()
What it means
CopilotClient.stop() performs graceful cleanup (closing sessions, shutting down RPC and the CLI process) and collects any exceptions raised by individual cleanup steps. If one or more steps failed, it raises an ExceptionGroup titled 'errors during CopilotClient.stop()' containing all of them. This follows Python 3.11+ exception-group semantics so partial failures are not silently swallowed.
Solutions
- Unpack the group: `except*` (3.11+) or iterate `e.exceptions` to see each failure.
- Call `await client.force_stop()` if graceful stop fails or hangs — it skips graceful cleanup.
- Inspect per-session health before stopping; close/abort problem sessions individually first.
- Upgrade the CLI/library if the failures point to known shutdown bugs.
Example fix
// before
await client.stop()
// after
try:
await client.stop()
except* Exception as eg:
for exc in eg.exceptions:
logging.warning("stop() sub-failure: %r", exc)
await client.force_stop() Defensive patterns
Strategy: try-catch
Try / catch
try:
await client.stop()
except ExceptionGroup as eg:
for exc in eg.exceptions:
logging.warning("stop() sub-failure: %r", exc)
await client.force_stop() Prevention
- Use `except*` (Python 3.11+) to handle individual sub-failures
- Fall back to force_stop() when graceful stop fails or times out
- Avoid calling stop() on an already-dead client; check state first
- Log session lifecycle to spot leaked sessions that break cleanup
When it happens
Trigger: Calling `await client.stop()` when one or more cleanup sub-operations raise — e.g. closing a session fails, the RPC connection is already broken, or terminating the CLI process errors.
Common situations: Network/process already dead so graceful shutdown can't complete; sessions leaked or in a bad state; calling stop() twice with an interrupted first stop; timeouts during graceful shutdown.
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
- Invalid entry '*': there is no bare wildcard. Use one or…
- Client is not connected. Call start() first.
- telemetry is not supported with…
- Set environment variables via either the client-level env…
- connectionToken must be a non-empty string
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/dc92cb66b2b201fa.
Report an issue: GitHub.
Appendix: source
Thrown at python/copilot/client.py:2175
)
except subprocess.TimeoutExpired as e:
errors.append(
StopError(
message=(
f"Timed out waiting for CLI process to exit after kill: {e}"
)
)
)
if self._process is self._cli_process:
self._process = None
self._cli_process = None
self._state = "disconnected"
if not self._is_external_server:
self._runtime_port = None
if errors:
raise ExceptionGroup("errors during CopilotClient.stop()", errors)
async def force_stop(self) -> None:
"""
Forcefully stop the CLI server without graceful cleanup.
Use this when :meth:`stop` fails or takes too long. This method:
- Clears all sessions immediately without destroying them
- Force closes the connection (closes the underlying transport)
- Kills the CLI process (if spawned by this client)
Example:
>>> # If normal stop hangs, force stop
>>> try:
... await asyncio.wait_for(client.stop(), timeout=5.0)
... except asyncio.TimeoutError:
... await client.force_stop()
"""
# Clear sessions immediately without trying to destroy themView on GitHub (pinned to cd8cf15dc3)