github/copilot-sdk · warning

failed to disconnect session

Error message

failed to disconnect session %s: %w

What it means

CopilotClient.Stop disconnects every registered session and wraps each Session.Disconnect failure with the session's ID. The wrapped error indicates one specific session failed to clean up while the overall Stop still proceeds over the other sessions.

Solutions

  1. Inspect the wrapped inner error and session ID to identify which session failed.
  2. Check that the runtime process/connection is healthy before stopping.
  3. Treat Stop as best-effort: resources are cleared from the client's session map regardless.
  4. Retry Stop only if the inner error indicates a transient network issue.

Example fix

// before
if err := client.Stop(ctx); err != nil { panic(err) }
// after
if err := client.Stop(ctx); err != nil {
    log.Printf("stop partially failed (resources still cleaned up): %v", err)
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := client.Stop(ctx); err != nil { for _, e := range unwrapAll(err) { log.Printf("session stop issue: %v", e) } }

Prevention

When it happens

Trigger: Calling Stop (or ForceStop) when one or more sessions' Disconnect RPC fails, e.g. because the runtime connection is already broken.

Common situations: Network drop before Stop; runtime process already exited; duplicate Stop calls where the second finds dead connections.

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 github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/9f2af0ee12105b6f. Report an issue: GitHub.

Appendix: source

Thrown at go/client.go:583

// Example:
//
//	if err := client.Stop(); err != nil {
//	    log.Printf("Cleanup error: %v", err)
//	}
func (c *Client) Stop() error {
	var errs []error

	// Disconnect all active sessions
	c.sessionsMux.Lock()
	sessions := make([]*Session, 0, len(c.sessions))
	for _, session := range c.sessions {
		sessions = append(sessions, session)
	}
	c.sessionsMux.Unlock()

	for _, session := range sessions {
		if err := session.Disconnect(); err != nil {
			errs = append(errs, fmt.Errorf("failed to disconnect session %s: %w", session.SessionID, err))
		}
	}

	c.sessionsMux.Lock()
	c.sessions = make(map[string]*Session)
	c.sessionsMux.Unlock()
	c.clearGitHubTokenProviders()

	c.startStopMux.Lock()
	defer c.startStopMux.Unlock()

	if (c.process != nil || c.ffiHost != nil) && !c.isExternalServer && c.RPC != nil {
		rpcClient := c.RPC
		runtimeShutdownStart := time.Now()
		shutdownDone := make(chan error, 1)
		go func() {
			_, err := rpcClient.Runtime.Shutdown(context.Background())
			shutdownDone <- err

View on GitHub (pinned to cd8cf15dc3)