helm/helm · error

kubernetes cluster unreachable

Error message

kubernetes cluster unreachable

What it means

Returned by Client.IsReachable (pkg/kube/client.go:258) when constructing the Kubernetes client fails with client-go's genericclioptions.ErrEmptyConfig — the kubeconfig is missing, empty, or has no usable current context. Helm deliberately replaces the cryptic ErrEmptyConfig with this friendly bare message (no wrapped detail). Other failures in IsReachable produce the wrapped variant "kubernetes cluster unreachable: <err>" from lines 261/264.

Source

Thrown at pkg/kube/client.go:258

func (c *Client) getKubeClient() (kubernetes.Interface, error) {
	if c.kubeClient != nil {
		return c.kubeClient, nil
	}
	kc, err := c.Factory.KubernetesClientSet()
	if err != nil {
		return nil, err
	}
	c.kubeClient = kc
	return c.kubeClient, nil
}

// IsReachable tests connectivity to the cluster.
func (c *Client) IsReachable() error {
	client, err := c.getKubeClient()
	if errors.Is(err, genericclioptions.ErrEmptyConfig) {
		// re-replace kubernetes ErrEmptyConfig error with a friendly error
		// moar workarounds for Kubernetes API breaking.
		return errors.New("kubernetes cluster unreachable")
	}
	if err != nil {
		return fmt.Errorf("kubernetes cluster unreachable: %w", err)
	}
	if _, err := client.Discovery().ServerVersion(); err != nil {
		return fmt.Errorf("kubernetes cluster unreachable: %w", err)
	}
	return nil
}

type clientCreateOptions struct {
	serverSideApply          bool
	forceConflicts           bool
	dryRun                   bool
	fieldValidationDirective FieldValidationDirective
}

type ClientCreateOption func(*clientCreateOptions) error

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Verify and regenerate kubeconfig: `kubectl config get-contexts` / `kubectl config use-context <name>`; check `echo $KUBECONFIG`
  2. Point KUBECONFIG at a valid file or create one (minikube/kind/cluster setup writes it)
  3. When using the SDK, pass a genericclioptions.RESTClientGetter (e.g. ConfigFlags with APIServer, BearerToken, CAFile set) instead of relying on the default config loading

Example fix

// before
c := kube.New(nil) // relies on default kubeconfig discovery
if err := c.IsReachable(); err != nil { return err }

// after — explicit config
flags := genericclioptions.NewConfigFlags(true)
flags.APIServer = ptr.To("https://127.0.0.1:6443")
flags.BearerToken = ptr.To("...")
flags.CAFile = ptr.To("/path/ca.crt")
c := kube.New(flags)
Defensive patterns

Strategy: validation

Validate before calling

// Validate kubeconfig usability before touching Helm
func kubeconfigUsable() error {
    if os.Getenv("KUBECONFIG") == "" {
        home, _ := os.UserHomeDir()
        if _, err := os.Stat(filepath.Join(home, ".kube", "config")); err != nil {
            return errors.New("no kubeconfig found: set KUBECONFIG or create ~/.kube/config")
        }
    }
    return nil
}

Try / catch

if err := c.IsReachable(); err != nil {
    if strings.Contains(err.Error(), "kubernetes cluster unreachable") {
        return fmt.Errorf("cannot reach cluster — check KUBECONFIG/context (kubectl get nodes): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: kube.Client.IsReachable() with KUBECONFIG unset/pointing at a nonexistent file, an empty kubeconfig, or no current-context; this is the preflight check behind `helm install/upgrade` reporting "Kubernetes cluster unreachable: ...".

Common situations: CI containers without a kubeconfig mounted; wrong KUBECONFIG path; a fresh machine that never ran kubectl config; a kubeconfig whose current-context points at a removed cluster; SDK usage without passing a RESTClientGetter.

Related errors


AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15). Data as JSON: /api/errors/ab8eb57f97110c2c. Report an issue: GitHub.