{"record":{"id":"42dcf1b9072137c6","repo":"JuliusBrussee/caveman","slug":"s-contains-no-valid-pem-certificate","errorCode":null,"errorMessage":"%s contains no valid PEM certificate","messagePattern":"(.+?) contains no valid PEM certificate","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/cabundle/cabundle.go","lineNumber":77,"sourceCode":"\t\tvar block *pem.Block\n\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: certificate %d is unparseable, so the bundle is incomplete and must not be half-trusted: %w\", path, len(certs)+1, err)\n\t\t}\n\t\tcerts = append(certs, cert)\n\t}\n\tif bytes.Contains(rest, []byte(\"-----BEGIN\")) {\n\t\treturn nil, fmt.Errorf(\"%s: trailing PEM block is truncated after %d certificate(s), so the bundle is incomplete and must not be half-trusted\", path, len(certs))\n\t}\n\tif len(certs) == 0 {\n\t\treturn nil, fmt.Errorf(\"%s contains no valid PEM certificate\", path)\n\t}\n\treturn certs, nil\n}\n","sourceCodeStart":59,"sourceCodeEnd":81,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/cabundle/cabundle.go#L59-L81","documentation":"loadCertificates parses a PEM bundle file and returns an error when it decodes zero usable certificates. The library throws it fail-closed: a bundle with no valid PEM blocks cannot be trusted to seed a root-CA pool, so callers get an error instead of an empty trust set.","triggerScenarios":"Calling loadRootCAs/Pool with a path whose file exists but contains no '-----BEGIN CERTIFICATE-----' blocks (e.g. an empty file, a text README, a private-key-only file, or a file with only truncated/garbage PEM).","commonSituations":"Config pointing at the wrong file (e.g. the key instead of the cert), an env var like SSL_CERT_FILE set to a nonexistent-format file, a bundle downloaded as HTML error page, or a truncated download leaving only partial PEM text (that case may instead hit the trailing-truncated error).","solutions":["Point the bundle path at a real CA bundle (e.g. /etc/ssl/certs/ca-certificates.crt) or the system trust store.","Open the file and verify it contains at least one complete '-----BEGIN CERTIFICATE-----' ... '-----END CERTIFICATE-----' block.","Re-download or re-export the bundle; confirm it is PEM (base64 with BEGIN/END headers), not DER or HTML.","If you only have DER, convert: openssl x509 -inform DER -in cert.der -out cert.pem."],"exampleFix":"// before\npool, err := NewPool(WithBundle(\"./notes.txt\")) // notes.txt has no PEM\n// after\npool, err := NewPool(WithBundle(\"/etc/ssl/certs/ca-certificates.crt\"))","handlingStrategy":"validation","validationCode":"func hasPEM(path string) bool {\n\tb, err := os.ReadFile(path)\n\treturn err == nil && bytes.Contains(b, []byte(\"-----BEGIN CERTIFICATE-----\"))\n}","typeGuard":null,"tryCatchPattern":"certs, err := loadCertificates(path)\nif err != nil {\n\treturn fmt.Errorf(\"CA bundle unusable, refusing to start: %w\", err)\n}","preventionTips":["Keep bundle paths in config validated at startup, not first TLS use","Ship a known-good bundle with the app as a fallback","Verify downloaded bundles contain at least one full PEM block before installing","Watch for download endpoints returning HTML error pages"],"tags":["pem","tls","certificate","fail-closed"],"backgroundTag":"invalid-argument-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}