kovidgoyal/kitty · error

Failed to connect to %s:%s with error: %w

Error message

Failed to connect to %s:%s with error: %w

What it means

net.Dial failed while connecting to the target network/address. The underlying error (wrapped with %w) says whether it was DNS resolution, connection refused, timeout, or network unreachable. This is the standard TCP/Unix dial failure path when fd passthrough mode is not in use.

Source

Thrown at tools/cmd/at/socket_io.go:180

func do_socket_io(io_data *rc_io_data) (serialized_response []byte, err error) {
	var conn net.Conn
	if global_options.to_network == "fd" {
		fd, _ := strconv.Atoi(global_options.to_address)
		if err != nil {
			return nil, err
		}
		f := os.NewFile(uintptr(fd), "fd:"+global_options.to_address)
		conn, err = net.FileConn(f)
		if err != nil {
			return nil, fmt.Errorf("Failed to open a socket for the remote control file descriptor: %d with error: %w", fd, err)
		}
		defer f.Close()
	} else {
		network := utils.IfElse(global_options.to_network == "ip", "tcp", global_options.to_network)
		conn, err = net.Dial(network, global_options.to_address)
		if err != nil {
			err = fmt.Errorf("Failed to connect to %s:%s with error: %w", network, global_options.to_address, err)
			return
		}
	}
	defer conn.Close()
	return simple_socket_io(&conn, io_data)
}

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Check the wrapped error: 'connection refused' means nothing is listening — start the peer or fix the port; 'no such host' means fix DNS; 'i/o timeout' means firewall/routing
  2. Verify the address format matches the network (host:port for tcp/ip, path for unix)
  3. Test reachability with nc/curl or telnet to the same address
  4. If it's a startup-order issue, retry with backoff or add a readiness check before dialing

Example fix

# before
at --to ip:myhost:9999
# after: confirm listener is up first
ss -ltnp | grep 9999   # then retry, or use the exact host:port it shows
at --to ip:127.0.0.1:9999
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: is anything listening?
if _, err := net.DialTimeout("tcp", addr, 2*time.Second); err != nil {
    log.Fatalf("peer %s unreachable: %v", addr, err)
}

Try / catch

var conn net.Conn
for attempt := 0; attempt < 5; attempt++ {
    conn, err = doSocketIO(opts)
    if err == nil { break }
    if !isTemporary(err) { break } // refused/timeout -> retry; DNS error -> fail fast
    time.Sleep(time.Duration(attempt+1) * 500 * time.Millisecond)
}

Prevention

When it happens

Trigger: Calling do_socket_io with to_network 'ip' (mapped to tcp) or another network and an address nothing is listening on, a hostname that doesn't resolve, a firewall dropping SYN packets (timeout), or the listener process not yet started.

Common situations: Service not started yet, wrong port in config, DNS pointing at a dead host, firewall/VPN blocking the port, or IPv6/IPv4 mismatch between resolved address and listener binding.

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/f0d8a72dabfd8887. Report an issue: GitHub.