{"record":{"id":"9d976d5ccb47cb1e","repo":"kubernetes/kops","slug":"error-deleting-node-q-v","errorCode":null,"errorMessage":"error deleting node %q: %v","messagePattern":"error deleting node %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/instancegroups/instancegroups.go","lineNumber":475,"sourceCode":"\t\t\t\t\treturn fmt.Errorf(\"failed to drain node %q: %w\", nodeName, err)\n\t\t\t\t}\n\t\t\t\tklog.Infof(\"Ignoring error draining node %q: %v\", nodeName, err)\n\t\t\t}\n\t\t} else {\n\t\t\tklog.Warningf(\"Skipping drain of instance %q, because it is not registered in kubernetes\", instanceID)\n\t\t}\n\t}\n\n\t// GCE often re-uses names, so we delete the node object to prevent the new instance from using the cordoned Node object\n\t// Scaleway has the same behavior\n\tif (c.Cluster.GetCloudProvider() == api.CloudProviderGCE || c.Cluster.GetCloudProvider() == api.CloudProviderScaleway) &&\n\t\t!isBastion && !c.CloudOnly {\n\t\tif u.Node == nil {\n\t\t\tklog.Warningf(\"no kubernetes Node associated with %s, skipping node deletion\", instanceID)\n\t\t} else {\n\t\t\tklog.Infof(\"deleting node %q from kubernetes\", nodeName)\n\t\t\tif err := c.deleteNode(ctx, u.Node); err != nil {\n\t\t\t\treturn fmt.Errorf(\"error deleting node %q: %v\", nodeName, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := c.deleteInstance(u); err != nil {\n\t\tklog.Errorf(\"error deleting instance %q, node %q: %v\", instanceID, nodeName, err)\n\t\treturn err\n\t}\n\n\tif err := c.reconcileInstanceGroup(ctx); err != nil {\n\t\tklog.Errorf(\"error reconciling instance group %q: %v\", u.CloudInstanceGroup.HumanName, err)\n\t\treturn err\n\t}\n\n\t// Wait for the minimum interval\n\tklog.Infof(\"waiting for %v after terminating instance\", sleepAfterTerminate)\n\ttime.Sleep(sleepAfterTerminate)\n","sourceCodeStart":457,"sourceCodeEnd":493,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/instancegroups/instancegroups.go#L457-L493","documentation":"After draining and terminating an instance, kOps deletes the corresponding Node object from the Kubernetes API. This error wraps a deleteNode failure and is always fatal to the rolling update of that instance, unlike drain/taint errors which can be ignored.","triggerScenarios":"c.deleteNode(ctx, u.Node) returns an error: the Node object no longer exists (404 handled?), RBAC denies node deletion, or the API server call fails. u.Node is non-nil (registered node) so the deletion path is taken.","commonSituations":"Node already garbage-collected by the node controller causing a conflict; API server outage mid-roll; service account lacking delete permission on nodes.","solutions":["Retry the rolling update; the node object may already be gone","Check RBAC delete permission on nodes","Verify API server connectivity","Manually delete the stale node object (kubectl delete node <name>) then re-run"],"exampleFix":"// before\n// stale node blocks the roll\n// after\nkubectl delete node <node-name> && kops rolling-update cluster --yes","handlingStrategy":"try-catch","validationCode":"// pre-check the node object exists and is deletable\n_, err := clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})\n// err == nil and no deletingFinalizers => safe to attempt delete","typeGuard":"func nodeExists(n *corev1.Node) bool { return n != nil && n.UID != \"\" }","tryCatchPattern":"if err := c.deleteNode(ctx, u.Node); err != nil {\n    if k8serrors.IsNotFound(err) { return nil } // already deleted; treat as success\n    return fmt.Errorf(\"error deleting node %q: %v\", nodeName, err)\n}","preventionTips":["Grant nodes delete RBAC to the operator identity","Avoid custom finalizers on kops-managed Node objects","Check API server health before starting a roll"],"tags":["kubernetes","node-deletion","rolling-update"],"backgroundTag":"kubernetes-api-request-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}