helm/helm · error

can't create TLS config for client: %w

Error message

can't create TLS config for client: %w

What it means

The OCI getter builds the same tlsutil.NewTLSConfig (cert/key pair, CA file, or insecure-skip-verify) when any TLS option is set, before creating the registry client. Failure means the TLS files could not be parsed or paired; the config is also given a ServerName extracted from the URL, so it applies specifically to the registry host being pulled from.

Source

Thrown at pkg/getter/ocigetter.go:147

			MaxIdleConns:          100,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
			Proxy:                 http.ProxyFromEnvironment,
			// Being nil would cause the tls.Config default to be used
			// "NewTLSConfig" modifies an empty TLS config, not the default one
			TLSClientConfig: &tls.Config{},
		}
	})

	if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" || g.opts.insecureSkipVerifyTLS {
		tlsConf, err := tlsutil.NewTLSConfig(
			tlsutil.WithInsecureSkipVerify(g.opts.insecureSkipVerifyTLS),
			tlsutil.WithCertKeyPairFiles(g.opts.certFile, g.opts.keyFile),
			tlsutil.WithCAFile(g.opts.caFile),
		)
		if err != nil {
			return nil, fmt.Errorf("can't create TLS config for client: %w", err)
		}

		sni, err := urlutil.ExtractHostname(g.opts.url)
		if err != nil {
			return nil, err
		}
		tlsConf.ServerName = sni

		g.transport.TLSClientConfig = tlsConf
	}

	opts := []registry.ClientOption{registry.ClientOptHTTPClient(&http.Client{
		Transport: g.transport,
		Timeout:   g.opts.timeout,
	})}
	if g.opts.plainHTTP {
		opts = append(opts, registry.ClientOptPlainHTTP())
	}

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Validate each file is PEM: `openssl x509 -in <file> -noout` (convert DER with `openssl x509 -inform der -outform pem`)
  2. Verify cert/key pairing with `openssl x509 -noout -modulus` vs `openssl rsa -noout -modulus`
  3. Use absolute paths and confirm the CI step actually copies all three files before helm runs
  4. For test clusters only: use --insecure-skip-tls-verify or plain-http instead of broken CA files
Defensive patterns

Strategy: validation

Validate before calling

if g.opts.certFile != "" || g.opts.keyFile != "" || g.opts.caFile != "" {
	for _, f := range []string{g.opts.certFile, g.opts.keyFile, g.opts.caFile} {
		if f == "" {
			continue
		}
		if _, err := os.Stat(f); err != nil {
			return fmt.Errorf("TLS file %s missing before OCI pull: %w", f, err)
		}
	}
}

Prevention

When it happens

Trigger: Running `helm pull oci://...` / dependency downloads with --ca-file, --cert-file/--key-file, or --insecure-skip-tls-verify where the files are missing, corrupt, or mismatched.

Common situations: Self-signed enterprise registries (Harbor, Artifactory) where the CA bundle was truncated or exported as DER instead of PEM; client-cert auth with an updated cert but stale key; CI caching TLS files incompletely.

Understand the failure class

Related errors


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