{"record":{"id":"9298abd6e34c010b","repo":"kubernetes/kops","slug":"node-not-set","errorCode":null,"errorMessage":"node not set","messagePattern":"node not set","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/instancegroups/instancegroups.go","lineNumber":676,"sourceCode":"\n\tif err := c.Cloud.DeleteInstance(u); err != nil {\n\t\tif nodeName != \"\" {\n\t\t\treturn fmt.Errorf(\"error deleting instance %q, node %q: %v\", id, nodeName, err)\n\t\t}\n\t\treturn fmt.Errorf(\"error deleting instance %q: %v\", id, err)\n\t}\n\n\treturn nil\n}\n\n// drainNode drains a K8s node.\nfunc (c *RollingUpdateCluster) drainNode(ctx context.Context, u *cloudinstances.CloudInstance) error {\n\tif c.K8sClient == nil {\n\t\treturn fmt.Errorf(\"K8sClient not set\")\n\t}\n\n\tif u.Node == nil {\n\t\treturn fmt.Errorf(\"node not set\")\n\t}\n\n\tif u.Node.Name == \"\" {\n\t\treturn fmt.Errorf(\"node name not set\")\n\t}\n\n\thelper := &drain.Helper{\n\t\tCtx:                 ctx,\n\t\tClient:              c.K8sClient,\n\t\tForce:               true,\n\t\tGracePeriodSeconds:  -1,\n\t\tIgnoreAllDaemonSets: true,\n\t\tOut:                 os.Stdout,\n\t\tErrOut:              os.Stderr,\n\t\tTimeout:             c.DrainTimeout,\n\n\t\t// The zero value would retry evictions without any delay\n\t\tEvictErrorRetryDelay: 5 * time.Second,","sourceCodeStart":658,"sourceCodeEnd":694,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/instancegroups/instancegroups.go#L658-L694","documentation":"drainNode also requires the CloudInstance to carry its corresponding Kubernetes Node object. This guard error means the instance has no Node set, so there is nothing to drain. Callers normally pre-check u.Node == nil and skip drain, so this surfaces only from direct/edge calls.","triggerScenarios":"drainNode called with a CloudInstance whose Node field is nil — e.g. an instance not yet registered in the cluster or discovered before node registration, when the caller bypasses the drainTerminateAndWait nil-check.","commonSituations":"Freshly launched instances queried before kubelet registration; control-plane instances without node objects in some topologies; library consumers calling drainNode directly.","solutions":["Skip draining when u.Node is nil (as drainTerminateAndWait does) and rely on cloud-level termination","Wait for the instance's kubelet to register before rolling it","Guard direct drainNode calls with a nil check on u.Node"],"exampleFix":"// before\nc.drainNode(ctx, u)\n// after\nif u.Node != nil {\n    c.drainNode(ctx, u)\n}","handlingStrategy":"validation","validationCode":"if u == nil || u.Node == nil {\n    klog.Warningf(\"Skipping drain of instance %q, not registered in kubernetes\", u.ID)\n    return nil\n}\nc.drainNode(ctx, u)","typeGuard":"func hasNode(u *cloudinstances.CloudInstance) bool {\n    return u != nil && u.Node != nil\n}","tryCatchPattern":"if err := c.drainNode(ctx, u); err != nil {\n    if err.Error() == \"node not set\" { return nil } // nothing registered to drain\n    return err\n}","preventionTips":["Check u.Node != nil before draining (mirror drainTerminateAndWait)","Wait for kubelet registration before rolling brand-new instances","Treat unregistered instances as cloud-only operations"],"tags":["kubernetes","drain","node","configuration"],"backgroundTag":"missing-k8s-node-object","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"}