{"record":{"id":"0e9ec723e68db96b","repo":"kubernetes/kops","slug":"unable-to-ssh-to-q-v","errorCode":null,"errorMessage":"unable to SSH to %q: %v","messagePattern":"unable to SSH to %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/dump/dumper.go","lineNumber":360,"sourceCode":"\n\t// HasBastion returns true if the sshClientFactory has a bastion configured.\n\t// Calling Dial with useBastion=true will return an error if there is no bastion.\n\tHasBastion() bool\n}\n\n// logDumperNode holds state for a particular node we are dumping\ntype logDumperNode struct {\n\tclient sshClient\n\tdumper *logDumper\n\n\tdir string\n}\n\n// connectToNode makes an SSH connection to the node and returns a logDumperNode\nfunc (d *logDumper) connectToNode(ctx context.Context, nodeName string, host string, useBastion bool) (*logDumperNode, error) {\n\tclient, err := d.sshClientFactory.Dial(ctx, host, useBastion)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to SSH to %q: %v\", host, err)\n\t}\n\treturn &logDumperNode{\n\t\tclient: client,\n\t\tdir:    filepath.Join(d.artifactsDir, nodeName),\n\t\tdumper: d,\n\t}, nil\n}\n\n// logDumperNode cleans up any state in the logDumperNode\nfunc (n *logDumperNode) Close() error {\n\treturn n.client.Close()\n}\n\n// dump captures the well-known set of logs\nfunc (n *logDumperNode) dump(ctx context.Context) []error {\n\tif ctx.Err() != nil {\n\t\treturn []error{ctx.Err()}\n\t}","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/dump/dumper.go#L342-L378","documentation":"Returned by logDumper.connectToNode (pkg/dump/dumper.go:360) when sshClientFactory.Dial fails to establish an SSH connection to the given host. It names the target host and embeds the low-level dial error (Dialer, x/crypto/ssh handshake, or the test double). It is subsequently wrapped by 'connecting: %w' in dumpNode, which swallows it into a klog warning path — this error means no logs could be collected for that node at all.","triggerScenarios":"d.sshClientFactory.Dial(ctx, host, useBastion) fails: TCP connect refused/timeout to the node IP, SSH authentication rejected (bad key/user), SSH version handshake failure, or Dial called with useBastion=true when no bastion is configured (HasBastion()==false).","commonSituations":"Node terminated/replaced so the cached IP no longer exists; security group or network ACL blocks port 22; dumping private nodes without a bastion; cluster SSH key deleted or ~/.ssh key not matching; newer node images changed the default SSH username.","solutions":["Test reachability of the host: `nc -vz <host> 22` or `ssh -v <user>@<host>`; fix security group/firewall rules to permit SSH.","Confirm the node IP is current (re-query cloud API / cluster state); stale IPs after node replacement are the most common cause.","Check SSH credentials: correct private key for the cluster and correct username for the image (ubuntu/admin/ec2-user).","If useBastion=true, ensure a bastion instance exists and is reachable, otherwise dial the node directly via its public address.","Retry after transient network issues; ensure the per-node timeout (nodeDumpTimeout) is not expiring mid-dial."],"exampleFix":"// before\nclient, err := d.sshClientFactory.Dial(ctx, host, useBastion)\nif err != nil {\n    return nil, fmt.Errorf(\"unable to SSH to %q: %v\", host, err)\n}\n// after (caller fallback: skip bastion when none configured)\nuseBastion := useBastion && d.sshClientFactory.HasBastion()\nclient, err := d.sshClientFactory.Dial(ctx, host, useBastion)\nif err != nil {\n    return nil, fmt.Errorf(\"unable to SSH to %q: %w\", host, err)\n}","handlingStrategy":"try-catch","validationCode":"// validate reachability and credentials before Dial\nif err := exec.Command(\"ssh\", \"-o\", \"BatchMode=yes\", \"-o\", \"ConnectTimeout=5\", user+\"@\"+host, \"true\").Run(); err != nil {\n    return fmt.Errorf(\"SSH to %s pre-check failed: %w\", host, err)\n}","typeGuard":null,"tryCatchPattern":"client, err := d.sshClientFactory.Dial(ctx, host, useBastion)\nif err != nil {\n    var netErr net.Error\n    switch {\n    case errors.As(err, &netErr) && netErr.Timeout():\n        klog.Warningf(\"SSH to %q timed out; check SG/firewall on port 22\", host)\n    case errors.Is(err, context.DeadlineExceeded):\n        klog.Warningf(\"SSH to %q hit dump timeout\", host)\n    default:\n        klog.Warningf(\"SSH to %q refused/auth failed: %v\", host, err)\n    }\n    return nil, err\n}","preventionTips":["Test `ssh -o BatchMode=yes user@host true` before running dumps to catch auth/key issues early.","Refresh node IPs from the cloud API; never reuse IPs cached from previous runs.","Configure security groups to allow SSH from the dump host or bastion.","Check HasBastion() before dialing with useBastion=true."],"tags":["ssh","network","authentication","go"],"backgroundTag":"ssh-connection-refused","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}