{"record":{"id":"124b3e18fa4abf37","repo":"temporalio/temporal","slug":"unable-to-read-client-certificate-file","errorCode":null,"errorMessage":"unable to read client certificate file","messagePattern":"unable to read client certificate file","errorType":"validation","errorClass":"ErrTLSConfig","httpStatus":null,"severity":"error","filePath":"common/auth/tls_config_helper.go","lineNumber":195,"sourceCode":"\t\t}\n\t\tif block.Type != \"CERTIFICATE\" || len(block.Headers) != 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tcertBytes := block.Bytes\n\t\treturn x509.ParseCertificates(certBytes)\n\t}\n\treturn nil, nil\n}\n\nfunc parseClientCert(temporalTls *TLS) (*tls.Certificate, error) {\n\tvar certBytes []byte\n\tvar keyBytes []byte\n\tvar err error\n\tif temporalTls.CertFile != \"\" {\n\t\tcertBytes, err = os.ReadFile(temporalTls.CertFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to read client certificate file\", err)\n\t\t}\n\t} else if temporalTls.CertData != \"\" {\n\t\tcertBytes, err = base64.StdEncoding.DecodeString(temporalTls.CertData)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to decode client certificate\", err)\n\t\t}\n\t}\n\n\tif temporalTls.KeyFile != \"\" {\n\t\tkeyBytes, err = os.ReadFile(temporalTls.KeyFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to read client certificate private key file\", err)\n\t\t}\n\t} else if temporalTls.KeyData != \"\" {\n\t\tkeyBytes, err = base64.StdEncoding.DecodeString(temporalTls.KeyData)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%w: %s (%w)\", ErrTLSConfig, \"unable to decode client certificate private key\", err)\n\t\t}","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/common/auth/tls_config_helper.go#L177-L213","documentation":"parseClientCert returns this error when CertFile is set but os.ReadFile fails to read the client certificate PEM file. The underlying os error is chained into the message and the whole thing is wrapped with ErrTLSConfig, so mTLS setup fails fast at config load.","triggerScenarios":"NewTLSConfig -> parseClientCert with CertFile pointing to a missing, deleted, or unreadable file (wrong path, wrong mount, permission denied, file is a directory).","commonSituations":"Typo in cert path; cert secret not mounted into the pod; permission issues for a non-root service user; relative path whose working directory differs in container vs local dev; file removed after a failed cert rotation.","solutions":["Check the chained os error: fix the path (ENOENT) or permissions (EACCES) accordingly.","Use an absolute path and confirm the cert is actually mounted/installed at that location in the deployment.","Correct the CertFile value in config or, if the cert is meant to be inline, remove CertFile and set base64 CertData.","Ensure rotation processes replace the file atomically so it never disappears mid-read."],"exampleFix":"// before\ntls:\n  certFile: \"/etc/temporal/certs/client.pem\"   # file not mounted in container\n// after (configmap/secret mount added, e.g. k8s):\n//   volumeMounts:\n//     - name: temporal-certs\n//       mountPath: /etc/temporal/certs\ntls:\n  certFile: \"/etc/temporal/certs/client.pem\"","handlingStrategy":"validation","validationCode":"func checkCertFileReadable(path string) error {\n\tinfo, err := os.Stat(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"certFile not accessible: %w\", err)\n\t}\n\tif info.IsDir() {\n\t\treturn fmt.Errorf(\"certFile is a directory: %s\", path)\n\t}\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"certFile not readable: %w\", err)\n\t}\n\treturn f.Close()\n}","typeGuard":null,"tryCatchPattern":"tlsCfg, err := auth.NewTLSConfig(cfg)\nif err != nil {\n\tif errors.Is(err, auth.ErrTLSConfig) {\n\t\t// message includes chained os error: ENOENT vs EACCES\n\t\tlogger.Error(\"client cert file could not be read\", tag.Key, err)\n\t}\n\treturn err\n}","preventionTips":["Mount client certs as secret volumes at fixed absolute paths and document them","Grant read access to the service's runtime user on cert files","Make cert rotation atomic (write temp file + rename) so the path is never missing","Stat the cert path in a pre-start check or readiness probe"],"tags":["tls","filesystem","config","mtls"],"backgroundTag":"file-not-found","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}