{"record":{"id":"c09d8405958d72ca","repo":"JuliusBrussee/caveman","slug":"s-s-contains-no-valid-pem-certificate","errorCode":null,"errorMessage":"%s (%s) contains no valid PEM certificate","messagePattern":"(.+?) \\((.+?)\\) contains no valid PEM certificate","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/chhttp/chhttp.go","lineNumber":191,"sourceCode":"\t\tblock, rest = pem.Decode(rest)\n\t\tif block == nil {\n\t\t\tbreak\n\t\t}\n\t\tif block.Type != \"CERTIFICATE\" {\n\t\t\tcontinue\n\t\t}\n\t\tcert, err := x509.ParseCertificate(block.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s (%s): certificate %d is unparseable, so the bundle is incomplete and must not be half-trusted: %w\", caFileEnv, path, added+1, err)\n\t\t}\n\t\troots.AddCert(cert)\n\t\tadded++\n\t}\n\tif bytes.Contains(rest, []byte(\"-----BEGIN\")) {\n\t\treturn nil, fmt.Errorf(\"%s (%s): trailing PEM block is truncated after %d certificate(s), so the bundle is incomplete and must not be half-trusted\", caFileEnv, path, added)\n\t}\n\tif added == 0 {\n\t\treturn nil, fmt.Errorf(\"%s (%s) contains no valid PEM certificate\", caFileEnv, path)\n\t}\n\treturn roots, nil\n}\n\n// errTransport fails every request with the configuration error that produced\n// it. A client constructor cannot return an error, and falling back to the\n// default transport would silently trade a rejected TLS configuration for\n// unpinned verification — so the client is built, and refuses to send.\ntype errTransport struct{ err error }\n\nfunc (t errTransport) RoundTrip(*http.Request) (*http.Response, error) { return nil, t.err }\n\n// queryTransport is the shared connection pool behind ClickHouse query clients.\n//\n// http.DefaultTransport keeps only DefaultMaxIdleConnsPerHost (2) idle\n// connections per host, so the 3rd+ concurrent ClickHouse read dials a fresh\n// connection and throws it away on completion — paying a TCP + TLS handshake per\n// query. Report handlers fan several queries out at once and production reads","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/chhttp/chhttp.go#L173-L209","documentation":"Thrown by rootsWithCAFile (chhttp.go:191) when the PEM decode loop finished without adding a single certificate: the file decoded, contained no unparseable CERTIFICATE blocks, but also zero CERTIFICATE-type blocks. Typical content is a PRIVATE KEY block, a CSR, or some other PEM/non-PEM payload. Setting a CA-file env var to such a file is treated as a configuration error, not an empty success.","triggerScenarios":"Pointing the CA-file env var at the wrong artifact: a TLS private key (-----BEGIN PRIVATE KEY-----), a certificate signing request, a keystore in another format (PKCS#12/.p12, DER binary), or a documentation file. All non-CERTIFICATE blocks are skipped by the loop, added stays 0, and this error returns.","commonSituations":"Confusing tls.key/tls.crt during setup; putting a .p12 file where PEM was expected; leaving a placeholder file from an install template; generating a key but never exporting the certificate.","solutions":["Confirm the file actually contains -----BEGIN CERTIFICATE----- blocks: grep 'BEGIN CERTIFICATE' <file>.","If the material is PKCS#12, convert it first: openssl pkcs12 -in store.p12 -nokeys -out bundle.pem.","If it is a DER certificate, convert: openssl x509 -inform der -in ca.der -out bundle.pem.","Point the env var at the CA certificate chain, never at the private key."],"exampleFix":"# before\nexport CLICKHOUSE_CA_FILE=/certs/tls.key   # wrong artifact\n\n# after\nexport CLICKHOUSE_CA_FILE=/certs/ca-bundle.pem  # contains CERTIFICATE blocks","handlingStrategy":"validation","validationCode":"data, _ := os.ReadFile(path)\nif !bytes.Contains(data, []byte(\"-----BEGIN CERTIFICATE-----\")) {\n    return fmt.Errorf(\"%s contains no PEM certificate; not a CA bundle\", path)\n}","typeGuard":null,"tryCatchPattern":"if err := validateBundleHasCerts(path); err != nil {\n    return err // do not default to an empty pool silently\n}","preventionTips":["Name CA bundles explicitly (ca-bundle.pem) and keys separately (tls.key) to avoid swaps.","Convert PKCS#12/DER to PEM at packaging time, not at runtime.","Assert 'BEGIN CERTIFICATE' appears in the file in your deploy scripts."],"tags":["go","tls","certificates","pem","config"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}