{"record":{"id":"257ec7596550654d","repo":"kubernetes/kops","slug":"connecting-w","errorCode":null,"errorMessage":"connecting: %w","messagePattern":"connecting: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/dump/dumper.go","lineNumber":311,"sourceCode":"// Large clusters dump multi-GB logs per node and need a higher value, configurable\n// via the --node-dump-timeout flag, because the files are dumped sequentially and a\n// single oversized log can otherwise exhaust the budget before the rest are read.\nconst defaultNodeDumpTimeout = time.Minute\n\n// DumpNode connects to a node and dumps the logs.\nfunc (d *logDumper) dumpNode(ctx context.Context, name string, ip string, useBastion bool) error {\n\tif ip == \"\" {\n\t\treturn fmt.Errorf(\"could not find address for %v, \", name)\n\t}\n\n\tklog.Infof(\"Dumping node %s\", name)\n\n\tctx, cancel := context.WithTimeout(ctx, d.nodeDumpTimeout)\n\tdefer cancel()\n\n\tn, err := d.connectToNode(ctx, name, ip, useBastion)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"connecting: %w\", err)\n\t}\n\n\t// As long as we connect to the node we will not return an error;\n\t// a failure to collect a log (or even any logs at all) is not\n\t// considered an error in dumping the node.\n\t// TODO(justinsb): clean up / rationalize\n\terrors := n.dump(ctx)\n\tfor _, e := range errors {\n\t\tklog.Warningf(\"error dumping node %s: %v\", name, e)\n\t}\n\n\tif err := n.Close(); err != nil {\n\t\tklog.Warningf(\"error closing connection: %v\", err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/dump/dumper.go#L293-L329","documentation":"This is the wrapper error returned by logDumper.dumpNode (pkg/dump/dumper.go:311) when the initial SSH connection to a cluster node fails during a log dump. dumpNode enforces a per-node timeout (default 1 minute, --node-dump-timeout) and delegates to connectToNode, which calls sshClientFactory.Dial. Any Dial failure — TCP refused, timeout, auth rejection, missing bastion — is wrapped here with %w so the underlying cause is preserved and can be unwrapped with errors.Is/As.","triggerScenarios":"dumpNode calls d.connectToNode(ctx, name, ip, useBastion) and the sshClientFactory.Dial call returns any error: connection refused, context deadline exceeded (nodeDumpTimeout hit), host unreachable, SSH handshake/auth failure, or Dial with useBastion=true when HasBastion() is false.","commonSituations":"Running `kops get cluster --yes ... -oyaml` style dump/`kops toolbox dump` against a terminated or non-routable node IP; security group blocks port 22; node is a private node whose bastion was deleted; SSH key for the cluster not present or wrong user; --node-dump-timeout too small for slow/large clusters.","solutions":["Read the wrapped cause (%w) with errors.Unwrap or by printing the full error; fix the underlying SSH failure (refused/unreachable/auth) first.","Verify network reachability: security groups/firewall allow SSH (22) from your host or via the bastion, and the node IP passed to dumpNode is current (nodes may have been replaced).","Confirm bastion configuration: useBastion=true requires HasBastion(); either configure a bastion or pass the public address so useBastion=false.","Check SSH credentials: the cluster SSH key exists locally and the username matches the node image (e.g. ubuntu/admin/ec2-user).","Increase --node-dump-timeout if the error wraps 'context deadline exceeded' for large clusters."],"exampleFix":"// before\nn, err := d.connectToNode(ctx, name, ip, useBastion)\nif err != nil {\n    return fmt.Errorf(\"connecting: %w\", err)\n}\n// after (caller-side: inspect wrapped cause)\nn, err := d.connectToNode(ctx, name, ip, useBastion)\nif err != nil {\n    var ctxErr context.Context\n    if errors.As(err, &ctxErr) || errors.Is(err, context.DeadlineExceeded) {\n        return fmt.Errorf(\"connecting to %s: timed out after %s; raise --node-dump-timeout: %w\", name, d.nodeDumpTimeout, err)\n    }\n    return fmt.Errorf(\"connecting to %s: %w\", name, err)\n}","handlingStrategy":"retry","validationCode":"// pre-check before dumping\nconn, err := net.DialTimeout(\"tcp\", ip+\":22\", 5*time.Second)\nif err != nil {\n    return fmt.Errorf(\"node %s unreachable on :22, skipping dump: %w\", name, err)\n}\nconn.Close()\nif useBastion && !d.sshClientFactory.HasBastion() {\n    useBastion = false\n}","typeGuard":null,"tryCatchPattern":"err := dumper.dumpNode(ctx, name, ip, useBastion)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        klog.Warningf(\"dump timed out for %s; raise --node-dump-timeout\", name)\n    } else if !errors.Is(err, context.Canceled) {\n        klog.Warningf(\"skipping node %s: %v\", name, err)\n    }\n}","preventionTips":["Pre-verify SSH reachability (TCP :22) for each node IP before starting the dump.","Keep the cluster SSH key and correct username configured before running dumps.","Set --node-dump-timeout generously for large clusters (dumps are sequential per node).","Ensure a bastion exists and is healthy whenever dumping private-only nodes."],"tags":["ssh","network","cluster-dump","go"],"backgroundTag":"ssh-connection-failed","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"}