ahmetb/kubectx · error

failed to create transport: %w

Error message

failed to create transport: %w

What it means

This error is returned by proxy.Start when rest.TransportFor(restCfg) fails to build an http.RoundTripper from the loaded kubeconfig REST config. TransportFor validates and materializes the TLS, client-cert, bearer-token, exec-credential, and proxy settings from the rest.Config, so any invalid or unusable credential material in the kubeconfig surfaces here. It is a wrapping error: the underlying client-go cause (e.g. x509 parse failure) is embedded via %w.

Source

Thrown at internal/proxy/readonly.go:78

// GET, HEAD, and OPTIONS requests (without protocol upgrades) to the real API server.
func Start(cfg Config) (*ReadonlyProxy, error) {
	loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.KubeconfigPath}
	overrides := &clientcmd.ConfigOverrides{CurrentContext: cfg.ContextName}
	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)

	restCfg, err := clientConfig.ClientConfig()
	if err != nil {
		return nil, fmt.Errorf("failed to load kubeconfig: %w", err)
	}

	targetURL, err := url.Parse(restCfg.Host)
	if err != nil {
		return nil, fmt.Errorf("failed to parse server URL %q: %w", restCfg.Host, err)
	}

	transport, err := rest.TransportFor(restCfg)
	if err != nil {
		return nil, fmt.Errorf("failed to create transport: %w", err)
	}

	handler := NewHandler(targetURL, transport)

	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return nil, fmt.Errorf("failed to listen: %w", err)
	}

	srv := &http.Server{Handler: handler}
	go srv.Serve(listener)

	debugLog.Printf("started on %s, proxying to %s", listener.Addr(), targetURL)

	return &ReadonlyProxy{
		server:   srv,
		listener: listener,
	}, nil

View on GitHub (pinned to 12ad6fb22e)

Solutions

  1. Run the exec/auth plugin manually (e.g. `aws eks get-token --cluster-name ...` or `gke-gcloud-auth-plugin`) to see the underlying cause and install/repair it if missing
  2. Inspect the current context's user/cluster in the kubeconfig (`kubectl config view --raw`) and fix or regenerate the certificate/key/CA data (`aws eks update-kubeconfig`, `gcloud container clusters get-credentials`, etc.)
  3. Check that certificate file paths in the kubeconfig exist and that cert and key match (compare modulus/fingerprints)
  4. Switch KUBECONFIG or --context to a known-good context to isolate whether the problem is credential material
  5. Set KUBECTX_DEBUG=1 and re-run; the wrapped client-go error text names the exact field that failed

Example fix

// before (exec plugin missing)
users:
- name: my-user
  user:
    exec:
      command: gke-gcloud-auth-plugin  # not installed
// after
// install the plugin first:
//   gcloud components install gke-gcloud-auth-plugin
users:
- name: my-user
  user:
    exec:
      command: /usr/local/bin/gke-gcloud-auth-plugin
      apiVersion: client.authentication.k8s.io/v1beta1
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: validate kubeconfig credential material before calling proxy.Start
func validateKubeconfig(path, ctxName string) error {
	loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: path}
	overrides := &clientcmd.ConfigOverrides{CurrentContext: ctxName}
	cc := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides)
	restCfg, err := cc.ClientConfig()
	if err != nil {
		return fmt.Errorf("kubeconfig load: %w", err)
	}
	if restCfg.Host == "" {
		return fmt.Errorf("context %q has no server", ctxName)
	}
	if restCfg.ExecProvider != nil {
		if _, err := exec.LookPath(restCfg.ExecProvider.Command); err != nil {
			return fmt.Errorf("exec plugin %q not installed", restCfg.ExecProvider.Command)
		}
	}
	return nil
}

Type guard

func isTransportConfigError(err error) bool {
	// narrow the wrapped cause before deciding how to react
	var ce *x509.CertificateInvalidError
	if errors.As(err, &ce) {
		return true // expired/untrusted cert: fix kubeconfig, no retry
	}
	return strings.Contains(err.Error(), "failed to create transport")
}

Try / catch

p, err := proxy.Start(cfg)
if err != nil {
	if strings.Contains(err.Error(), "failed to create transport") {
		var pe *exec.Error
		if errors.As(err, &pe) {
			return fmt.Errorf("auth plugin %s not found: %w", pe.Name, err)
		}
		return fmt.Errorf("bad credential material in kubeconfig: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: rest.TransportFor fails when the kubeconfig context referenced by Config.ContextName points to a cluster/user with malformed TLS data (bad certificate or key PEM), a client cert/key pair that does not match, an exec credential plugin that is missing, not executable, or exits non-zero, or an invalid CA file path / proxy URL.

Common situations: Kubeconfig generated by an older cluster bootstrap with expired or corrupt client certificates; KUBECONFIG pointing at a context whose user uses an exec plugin (aws eks get-token, gke-gcloud-auth-plugin) that is not installed or not on PATH; a certificate-authority path moved or deleted; hand-edited kubeconfig with base64 padding mistakes.

Related errors


AI-assisted analysis of ahmetb/kubectx@12ad6fb22e (2026-09-02). Data as JSON: /api/errors/37f1167af177f1fd. Report an issue: GitHub.