chenhg5/cc-connect · error

process exited

Error message

process exited

What it means

When the Copilot CLI child process terminates, readLoop's deferred cleanup marks the session dead and cancels every in-flight RPC with the sentinel error 'process exited'. Any caller blocked on a pending RPC (handshake, send, permission response) receives this error, indicating the transport died because the process is gone.

Source

Thrown at agent/copilot/session.go:251

		EnvValueMode:                   "direct",
		EnableConfigDiscovery:          &enableConfigDiscovery,
	}
}

func newCopilotSessionID() string {
	var b [16]byte
	if _, err := rand.Read(b[:]); err != nil {
		return fmt.Sprintf("cc-connect-%d", time.Now().UnixNano())
	}
	b[6] = (b[6] & 0x0f) | 0x40
	b[8] = (b[8] & 0x3f) | 0x80
	return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16])
}

func (cs *copilotSession) readLoop(stderrBuf *bytes.Buffer) {
	defer func() {
		cs.alive.Store(false)
		cs.rpc.cancelAll(fmt.Errorf("process exited"))

		// Wait for process exit
		if err := cs.cmd.Wait(); err != nil {
			stderrMsg := strings.TrimSpace(stderrBuf.String())
			if stderrMsg != "" {
				slog.Error("copilotSession: process failed", "error", err, "stderr", stderrMsg)
				evt := core.Event{Type: core.EventError, Error: fmt.Errorf("%s", stderrMsg)}
				select {
				case cs.events <- evt:
				case <-cs.ctx.Done():
				}
			}
		}
		close(cs.events)
		close(cs.done)
	}()

	for {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check the log line 'copilotSession: process failed' that follows — it carries the Wait() error and the CLI's stderr, which names the real cause.
  2. Fix the underlying CLI failure (missing binary, auth, bad flag) revealed by the stderr text, then restart the session.
  3. Restart the session (/new or reconnect) — pending RPCs cannot recover once the process is gone.
  4. If OOM-killed, reduce concurrency/memory pressure or move the CLI to a larger host.
  5. If killed by systemd/launchd, raise the service memory limit or stop-timeout so the CLI is not terminated mid-request.

Example fix

// before: CLI killed by systemd on stop
# systemctl stop cc-connect  -> CLI gets SIGTERM mid-RPC -> "process exited"
// after: give the service a graceful shutdown window
# cc-connect.service
[Service]
TimeoutStopSec=30
KillMode=mixed
Defensive patterns

Strategy: fallback

Validate before calling

if !sessionAlive(session) {
    session = agent.StartSession(ctx, opts)
}

Type guard

func sessionAlive(s core.AgentSession) bool {
    if a, ok := s.(interface{ IsAlive() bool }); ok {
        return a.IsAlive()
    }
    return true
}

Try / catch

for evt := range session.Events() {
    if evt.Type == core.EventError && strings.Contains(fmt.Sprint(evt.Error), "process exited") {
        slog.Error("copilot CLI died; restarting session")
        session = agent.StartSession(ctx, opts)
    }
}

Prevention

When it happens

Trigger: readLoop's deferred func runs after the CLI's stdout closes: cs.alive.Store(false) then cs.rpc.cancelAll(fmt.Errorf("process exited")); surfaces on any pending rpc.call waiter — e.g. an in-flight session.create/send — when the CLI crashes, is killed (OOM, signal), or exits voluntarily.

Common situations: Copilot CLI crashes on a malformed request; host OOM-kills the CLI; the process is killed by an external signal (systemd stop, user kill); CLI exits immediately due to missing binary/arg incompatibility at startup; abrupt disconnect of the controlling terminal.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/5f4571ea1f819175. Report an issue: GitHub.