kubernetes/kops · error
K8sClient not set
Error message
K8sClient not set
What it means
drainNode requires a Kubernetes client to evict pods from the node. This guard error means RollingUpdateCluster was constructed without a K8sClient, so draining is impossible. It propagates into drainTerminateAndWait and (with FailOnDrainError) fails the roll.
Source
Thrown at pkg/instancegroups/instancegroups.go:672
klog.Infof("Stopping instance %q, node %q, in group %q (this may take a while).", id, nodeName, u.CloudInstanceGroup.HumanName)
} else {
klog.Infof("Stopping instance %q, in group %q (this may take a while).", id, u.CloudInstanceGroup.HumanName)
}
if err := c.Cloud.DeleteInstance(u); err != nil {
if nodeName != "" {
return fmt.Errorf("error deleting instance %q, node %q: %v", id, nodeName, err)
}
return fmt.Errorf("error deleting instance %q: %v", id, err)
}
return nil
}
// drainNode drains a K8s node.
func (c *RollingUpdateCluster) drainNode(ctx context.Context, u *cloudinstances.CloudInstance) error {
if c.K8sClient == nil {
return fmt.Errorf("K8sClient not set")
}
if u.Node == nil {
return fmt.Errorf("node not set")
}
if u.Node.Name == "" {
return fmt.Errorf("node name not set")
}
helper := &drain.Helper{
Ctx: ctx,
Client: c.K8sClient,
Force: true,
GracePeriodSeconds: -1,
IgnoreAllDaemonSets: true,
Out: os.Stdout,
ErrOut: os.Stderr,View on GitHub (pinned to 4c8573c808)
Solutions
- Ensure K8sClient is set when constructing RollingUpdateCluster (clientset from a valid kubeconfig)
- If no cluster access is intended, use --cloud-only so drain is skipped
- Verify kubeconfig availability/validity for the target cluster
Example fix
// before
ru := &instancegroups.RollingUpdateCluster{Cloud: cloud, ...} // K8sClient nil
// after
k8sClient, err := kubernetes.NewForConfig(config)
ru := &instancegroups.RollingUpdateCluster{Cloud: cloud, K8sClient: k8sClient, ...} Defensive patterns
Strategy: validation
Validate before calling
if c.K8sClient == nil {
clientset, err := kubernetes.NewForConfig(restConfig)
if err != nil { return err }
c.K8sClient = clientset
} Type guard
func drainable(c *RollingUpdateCluster) bool { return c != nil && c.K8sClient != nil } Try / catch
if err := c.drainNode(ctx, u); err != nil {
if err.Error() == "K8sClient not set" {
return fmt.Errorf("cannot drain: configure a Kubernetes clientset or use --cloud-only")
}
return err
} Prevention
- Always populate K8sClient when constructing RollingUpdateCluster
- Use --cloud-only explicitly when no cluster API access is intended
- Validate kubeconfig resolution before starting a roll
When it happens
Trigger: Rolling update invoked without building a kubeconfig-backed clientset — e.g. --cloud-only flows or programmatic use that leaves K8sClient nil while still attempting to drain a registered node.
Common situations: Programmatic/library use of RollingUpdateCluster forgetting to set K8sClient; kops run in an environment where no kubeconfig can be resolved; intentionally cloud-only operations hitting the drain path.
Related errors
- node not set
- building kubernetes client for node labeler: %w
- clientset is not initialized
- getting kubernetes client: %w
- cannot create cluster validator: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/1d270fb52f659b82.
Report an issue: GitHub.