{"record":{"id":"642bcc173b5b265e","repo":"helm/helm","slug":"can-t-create-tls-config-for-client-w-642bcc","errorCode":null,"errorMessage":"can't create TLS config for client: %w","messagePattern":"can't create TLS config for client: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/getter/ocigetter.go","lineNumber":147,"sourceCode":"\t\t\tMaxIdleConns:          100,\n\t\t\tIdleConnTimeout:       90 * time.Second,\n\t\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\t\tExpectContinueTimeout: 1 * time.Second,\n\t\t\tProxy:                 http.ProxyFromEnvironment,\n\t\t\t// Being nil would cause the tls.Config default to be used\n\t\t\t// \"NewTLSConfig\" modifies an empty TLS config, not the default one\n\t\t\tTLSClientConfig: &tls.Config{},\n\t\t}\n\t})\n\n\tif (g.opts.certFile != \"\" && g.opts.keyFile != \"\") || g.opts.caFile != \"\" || g.opts.insecureSkipVerifyTLS {\n\t\ttlsConf, err := tlsutil.NewTLSConfig(\n\t\t\ttlsutil.WithInsecureSkipVerify(g.opts.insecureSkipVerifyTLS),\n\t\t\ttlsutil.WithCertKeyPairFiles(g.opts.certFile, g.opts.keyFile),\n\t\t\ttlsutil.WithCAFile(g.opts.caFile),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"can't create TLS config for client: %w\", err)\n\t\t}\n\n\t\tsni, err := urlutil.ExtractHostname(g.opts.url)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConf.ServerName = sni\n\n\t\tg.transport.TLSClientConfig = tlsConf\n\t}\n\n\topts := []registry.ClientOption{registry.ClientOptHTTPClient(&http.Client{\n\t\tTransport: g.transport,\n\t\tTimeout:   g.opts.timeout,\n\t})}\n\tif g.opts.plainHTTP {\n\t\topts = append(opts, registry.ClientOptPlainHTTP())\n\t}","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/helm/helm/blob/2a29f1770b62844b27197d2507377361d45ad7c0/pkg/getter/ocigetter.go#L129-L165","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if g.opts.certFile != \"\" || g.opts.keyFile != \"\" || g.opts.caFile != \"\" {\n\tfor _, f := range []string{g.opts.certFile, g.opts.keyFile, g.opts.caFile} {\n\t\tif f == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := os.Stat(f); err != nil {\n\t\t\treturn fmt.Errorf(\"TLS file %s missing before OCI pull: %w\", f, err)\n\t\t}\n\t}\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["tls","certificates","oci","registry","configuration","helm"],"backgroundTag":null,"analyzedSha":"2a29f1770b62844b27197d2507377361d45ad7c0","analyzedAt":"2026-08-15T22:02:47.490Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}