{"record":{"id":"af609a56bde79eeb","repo":"JuliusBrussee/caveman","slug":"s-certificate-d-is-unparseable-so-the-bundle-is-incomplete","errorCode":null,"errorMessage":"%s: certificate %d is unparseable, so the bundle is incomplete and must not be half-trusted: %w","messagePattern":"(.+?): certificate (.+?) is unparseable, so the bundle is incomplete and must not be half-trusted: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/cabundle/cabundle.go","lineNumber":69,"sourceCode":"func Certificates(path string) ([]*x509.Certificate, error) {\n\tbundle, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar certs []*x509.Certificate\n\trest := bundle\n\tfor {\n\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":51,"sourceCodeEnd":81,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/cabundle/cabundle.go#L51-L81","documentation":"Certificates parses a PEM file at path; when a CERTIFICATE block's DER bytes fail x509.ParseCertificate, it fails closed instead of skipping the bad entry, since trusting the remaining certs would make the bundle incomplete and half-trusted. The error names the file, the 1-based certificate position, and wraps the underlying parse error.","triggerScenarios":"Calling Certificates (or Pool/loadRootCAs which call it) on a PEM file containing a block labeled CERTIFICATE whose bytes are corrupt, truncated, or not DER (e.g. the file contains a PRIVATE KEY body mislabeled, or was mangled by copy-paste/encoding conversion).","commonSituations":"Mounting a truncated Kubernetes secret or ConfigMap; a cert file saved with Windows line-ending or base64 re-encoding corruption; concatenating a full-chain file where one intermediate was cut off mid-base64; passing a private key file where a certificate was expected.","solutions":["Verify each PEM block with `openssl x509 -in bundle.pem` per block (or `openssl crl2pkcs7 -nocrl -certfile bundle.pem | openssl pkcs7 -print_certs`) to find the corrupt certificate.","Re-export/re-download the offending certificate from its source; replace the file rather than editing base64 by hand.","Check the number of certs (openssl grep -c 'BEGIN CERTIFICATE') against what the CA provided; a truncated copy/paste is the usual culprit.","If the file is actually a private key or CSR, obtain the correct certificate file and update the configured path."],"exampleFix":"// before\nroots, err := cabundle.PoolOf(mustParse(\"/etc/certs/bundle.pem\")) // cert #2 corrupt\n// after\n// regenerate bundle from known-good certs:\n//   cat root.pem intermediate.pem > bundle.pem && openssl x509 -in bundle.pem -noout\nroots, err := cabundle.PoolOf(certs)\nif err != nil { return fmt.Errorf(\"tls setup: %w\", err) }","handlingStrategy":"validation","validationCode":"func validateBundle(path string) error {\n    data, err := os.ReadFile(path)\n    if err != nil { return err }\n    rest := data\n    for {\n        var block *pem.Block\n        block, rest = pem.Decode(rest)\n        if block == nil { break }\n        if block.Type != \"CERTIFICATE\" { continue }\n        if _, err := x509.ParseCertificate(block.Bytes); err != nil {\n            return fmt.Errorf(\"%s has a corrupt certificate: %w\", path, err)\n        }\n    }\n    return nil\n}\n// call validateBundle before cabundle.Certificates/PoolOf","typeGuard":null,"tryCatchPattern":"certs, err := cabundle.Certificates(path)\nif err != nil && strings.Contains(err.Error(), \"unparseable\") {\n    return fmt.Errorf(\"TLS bundle %s is corrupt; regenerate it from the CA source: %w\", path, err)\n}","preventionTips":["Validate bundles with `openssl x509`/`crl2pkcs7` in CI before deploying.","Never paste-edit base64; always regenerate files from the CA's original output.","Verify the file is a certificate bundle, not a key or CSR (block type check).","Keep the app fail-closed: never strip bad certs to 'make it work'."],"tags":["tls","certificates","pem","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"}