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
- Validate each file is PEM: `openssl x509 -in <file> -noout` (convert DER with `openssl x509 -inform der -outform pem`)
- Verify cert/key pairing with `openssl x509 -noout -modulus` vs `openssl rsa -noout -modulus`
- Use absolute paths and confirm the CI step actually copies all three files before helm runs
- 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
- Store the registry CA/client-cert set as a unit (same secret/configmap) so files never drift apart
- Prefer PEM output when exporting certificates from vaults and browsers
- Smoke-test TLS setup with a one-shot `helm pull oci://... --ca-file ...` in CI before deployments
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- can't create TLS config for client: %w
- missing registry client: %w
- missing registry client: %w
- missing registry client: %w
- missing registry client: %w
AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15).
Data as JSON: /api/errors/642bcc173b5b265e.
Report an issue: GitHub.