github/copilot-sdk · error

failed to connect to CLI server at

Error message

failed to connect to CLI server at %s: %w

What it means

Returned by connectViaTCP when the TCP dial to host:port fails within the 10-second dial timeout. The original dial error (refused, timeout, unreachable, DNS failure) is wrapped, so %w carries the precise cause.

Solutions

  1. Verify the CLI server process is running and listening on the reported port (e.g. lsof -i :PORT, or `nc -zv host port`).
  2. Check the host value: use 127.0.0.1 instead of localhost to rule out IPv6 resolution issues.
  3. Confirm firewalls/security groups allow TCP traffic to the port, especially for remote/container targets.
  4. Re-read the server's current port (it may change per launch) and retry with the fresh value.
  5. Inspect the wrapped error to distinguish connection refused (not listening) from timeout (firewall/routing).

Example fix

// before
c := client.New(client.WithHost("localhost"), client.WithPort(8080))

// after
if err := net.DialTimeout("tcp", "127.0.0.1:8080", 2*time.Second); err != nil {
    log.Fatalf("CLI server not reachable, is it running? %v", err)
}
c := client.New(client.WithHost("127.0.0.1"), client.WithPort(8080))
Defensive patterns

Strategy: retry

Validate before calling

conn, err := net.DialTimeout("tcp", addr, 2*time.Second)
if err != nil { return fmt.Errorf("server not reachable at %s: %w", addr, err) }
conn.Close()

Try / catch

if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) && netErr.Timeout() {
        // retry with backoff or check firewall
    }
    return err
}

Prevention

When it happens

Trigger: Client.connectViaTCP dialing net.JoinHostPort(actualHost, actualPort) fails: server not listening on that port, wrong host, firewall, or the 10s context deadline expires before the handshake completes.

Common situations: CLI server crashed or was never started; port number stale after server restart; connecting to a container/remote host where the port isn't exposed; IPv4/IPv6 mismatch on 'localhost'; corporate firewall blocking the port.

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/1b2c0d00f8f482f5. Report an issue: GitHub.

Appendix: source

Thrown at go/client.go:2446

	// Connect via TCP
	return c.connectViaTCP(ctx)
}

// connectViaTCP connects to the CLI server via TCP socket.
func (c *Client) connectViaTCP(ctx context.Context) error {
	if c.actualPort == 0 {
		return fmt.Errorf("server port not available")
	}

	// Merge a 10-second timeout with the caller's context so whichever
	// deadline comes first wins.
	address := net.JoinHostPort(c.actualHost, fmt.Sprintf("%d", c.actualPort))
	dialCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
	defer cancel()
	var dialer net.Dialer
	conn, err := dialer.DialContext(dialCtx, "tcp", address)
	if err != nil {
		return fmt.Errorf("failed to connect to CLI server at %s: %w", address, err)
	}

	c.conn = conn

	// Create JSON-RPC client with the connection
	c.client = jsonrpc2.NewClient(conn, conn)
	if c.processDone != nil {
		c.client.SetProcessDone(c.processDone, c.processErrorPtr)
	}
	c.client.SetOnClose(c.handleConnectionClose)
	c.RPC = rpc.NewServerRPC(c.client)
	c.internalRPC = rpc.NewInternalServerRPC(c.client)
	c.setupNotificationHandler()
	c.client.Start()

	return nil
}

View on GitHub (pinned to cd8cf15dc3)