github/copilot-sdk · error · IOException

CLI process exited unexpectedly. stderr

Error message

CLI process exited unexpectedly.
stderr: {stderrOutput}

What it means

StartAsync wraps an unexpected CLI process exit in an IOException that appends the collected stderr output. When the child CLI process dies during startup or operation and its stderr contains diagnostics, the library rethrows as 'CLI process exited unexpectedly.\nstderr: ...' with the original exception as the inner exception, so the underlying failure reason is preserved in the stderr text.

Solutions

  1. Read the 'stderr:' section of the message - it contains the actual crash reason from the CLI.
  2. Verify the CLI is installed, executable, and a version compatible with the SDK.
  3. Run the CLI manually with the same arguments to reproduce and inspect the failure.
  4. Retry via EnsureConnectedAsync after fixing the underlying cause; check innerException for the original exit details.

Example fix

// before
var client = new CopilotClient(options, RuntimeConnection.ForStdio()); // old CLI version crashes on startup
// after
// 1. inspect stderr in the IOException message
try { await client.StartAsync(); }
catch (IOException ex) { Console.WriteLine(ex.Message); } // shows CLI stderr
// 2. update the CLI to a compatible version, then retry
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the CLI runs before connecting:
var psi = new ProcessStartInfo(cliPath, "--version") { RedirectStandardError = true };
using var p = Process.Start(psi);
if (p is null || p.WaitForExit(5000) && p.ExitCode != 0)
    throw new InvalidOperationException("CLI is not healthy before start.");

Type guard

null

Try / catch

try { await client.StartAsync(); }
catch (IOException ex) when (ex.Message.StartsWith("CLI process exited unexpectedly"))
{
    var stderr = ex.Message[(ex.Message.IndexOf("stderr:") + 7)..];
    logger.LogError(ex, "CLI crashed. stderr: {Stderr}", stderr);
}

Prevention

When it happens

Trigger: Calling StartAsync/EnsureConnectedAsync (or any operation over an active session) when the spawned CLI process crashes or exits early; the stderr pump buffer is non-empty and the exception is not already a stderr-formatted message.

Common situations: Unsupported or corrupt CLI installation; missing runtime dependencies for the CLI binary; port conflicts or startup crashes printed to stderr; the CLI being killed by the OS; version mismatch between SDK and CLI.

Related errors


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

Appendix: source

Thrown at dotnet/src/Client.cs:515

                if (connection is not null)
                {
                    await CleanupConnectionAsync(connection, errors: null, gracefulRuntimeShutdown: false);
                }
                else if (cliProcess is not null)
                {
                    await CleanupCliProcessAsync(cliProcess, stderrPump, errors: null, _logger);
                }

                if (ex is IOException
                    && cliProcess is not null
                    && stderrPump is not null
                    && !ex.Message.Contains("stderr:", StringComparison.OrdinalIgnoreCase))
                {
                    var stderrOutput = GetStderrOutput(stderrPump.Buffer);
                    if (!string.IsNullOrEmpty(stderrOutput))
                    {
                        throw new IOException(
                            FormatCliExitedMessage("CLI process exited unexpectedly.", stderrOutput),
                            ex);
                    }
                }

                throw;
            }
        }
    }

    /// <summary>
    /// Disconnects from the Copilot server and closes all active sessions.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
    /// <remarks>
    /// <para>
    /// This method performs graceful cleanup:
    /// <list type="number">

View on GitHub (pinned to cd8cf15dc3)