netbirdio/netbird · error

dial %s: %w

Error message

dial %s: %w

What it means

Returned when sshclient.Dial cannot establish the NetBird SSH session to target (host:port built from the host argument and the --port flag, default 22). Dial connects through the local netbird daemon, performs management login (possibly browser-based SSO), fetches the peer's key via the daemon, verifies it against known_hosts, and establishes the SSH transport — any failure in that chain surfaces here.

Source

Thrown at client/cmd/ssh.go:542

func runSSH(ctx context.Context, addr string, cmd *cobra.Command) error {
	target := net.JoinHostPort(strings.Trim(addr, "[]"), strconv.Itoa(port))
	c, err := sshclient.Dial(ctx, target, username, sshclient.DialOptions{
		KnownHostsFile:     knownHostsFile,
		IdentityFile:       identityFile,
		DaemonAddr:         daemonAddr,
		SkipCachedToken:    skipCachedToken,
		InsecureSkipVerify: !strictHostKeyChecking,
		NoBrowser:          sshNoBrowser,
	})

	if err != nil {
		cmd.Printf("Failed to connect to %s@%s\n", username, target)
		cmd.Printf("\nTroubleshooting steps:\n")
		cmd.Printf("  1. Check peer connectivity: netbird status -d\n")
		cmd.Printf("  2. Verify SSH server is enabled on the peer\n")
		cmd.Printf("  3. Ensure correct hostname/IP is used\n")
		return fmt.Errorf("dial %s: %w", target, err)
	}

	sshCtx, cancel := context.WithCancel(ctx)
	defer cancel()

	go func() {
		<-sshCtx.Done()
		if err := c.Close(); err != nil {
			cmd.Printf("Error closing SSH connection: %v\n", err)
		}
	}()

	if err := startPortForwarding(sshCtx, c, cmd); err != nil {
		return fmt.Errorf("start port forwarding: %w", err)
	}

	if command != "" {
		return executeSSHCommand(sshCtx, c, command)

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Check peer connectivity first: `netbird status -d` and confirm the peer shows Connected and SSH enabled.
  2. Verify the target hostname/IP and port: resolve the peer's NetBird IP or hostname, and pass --port if the remote SSH server is not on 22.
  3. Ensure the local netbird daemon/service is running (`systemctl status netbird` or check the service) and that you are logged in (`netbird login status-check`).
  4. On first connect to a new peer, let the key be recorded or explicitly relax checking once with --insecure-on-host-key-mismatch / the documented non-strict flag; never leave it relaxed permanently.
  5. Retry after confirming management shows the peer online; transient overlay drops usually clear within a renegotiation interval.

Example fix

# before
netbird ssh peer-host
# -> dial peer-host:22: <underlying cause>

# after
netbird status -d            # confirm peer is Connected, ssh enabled
netbird ssh --port 22 peer-host  # re-run once peer is reachable
# if the key is unknown/stale, refresh known hosts per docs instead of skipping verification
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: daemon reachable, peer known, target resolvable
func sshPreFlight(host string, port int) error {
	if _, err := nbclient.Status(ctx); err != nil {
		return fmt.Errorf("netbird daemon down: %w", err) // start service first
	}
	peer := findPeerByFqdnOrIP(host) // via status -d data
	if peer == nil || !peer.Connected {
		return fmt.Errorf("peer %s offline or unknown", host)
	}
	if port < 1 || port > 65535 {
		return fmt.Errorf("bad port %d", port)
	}
	return nil
}

Try / catch

var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
	// transient overlay drop: retry with backoff (1s, 5s) before giving up
}
if strings.Contains(err.Error(), "knownhosts") || strings.Contains(err.Error(), "host key") {
	// identity problem: surface to the user, do NOT auto-disable verification
}

Prevention

When it happens

Trigger: `netbird ssh <host>` when the peer is offline or unreachable over the overlay, the SSH server is not enabled on the remote peer (management setting), the host/IP is wrong, the local daemon is not running (unix socket / npipe unreachable), login to management fails or the browser flow is cancelled, or strict host key checking rejects an unknown/changed key.

Common situations: Peers that connected hours ago but dropped (NAT timeout, machine asleep); SSH disabled in the peer's group policy; first connection with StrictHostKeyChecking rejecting an unseen host key; running ssh before `netbird up`; expired management session with NoBrowser set in a headless script.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/8d8183d390a95c7f. Report an issue: GitHub.