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
- Verify the CLI server process is running and listening on the reported port (e.g. lsof -i :PORT, or `nc -zv host port`).
- Check the host value: use 127.0.0.1 instead of localhost to rule out IPv6 resolution issues.
- Confirm firewalls/security groups allow TCP traffic to the port, especially for remote/container targets.
- Re-read the server's current port (it may change per launch) and retry with the fresh value.
- 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
- Health-check the host:port with a quick dial before connecting.
- Prefer 127.0.0.1 over localhost to avoid IPv6 resolution surprises.
- Re-read the server port after every server restart.
- Confirm firewall/security-group rules for remote targets.
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
- Failed to connect to CLI server at
- failed to close socket
- server port not available
- Server port not available
- Cannot connect because TCP host or port are not available
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)