{"record":{"id":"2ac9fe0041833a4e","repo":"JuliusBrussee/caveman","slug":"s-s-certificate-d-is-unparseable-so-the-bun","errorCode":null,"errorMessage":"%s (%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/chhttp/chhttp.go","lineNumber":182,"sourceCode":"\t}\n\troots, err := x509.SystemCertPool()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: load system certificate pool: %w\", caFileEnv, err)\n\t}\n\tadded := 0\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 (%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 }","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/chhttp/chhttp.go#L164-L200","documentation":"Thrown while parsing the custom CA bundle in rootsWithCAFile (chhttp.go:182): a PEM block of type CERTIFICATE was decoded, but x509.ParseCertificate rejected its DER bytes. Because a half-loaded bundle would make only some endpoints trust the issuer (failures surfacing later as fake network faults at telemetry flush), the whole bundle is rejected CLOSED at boot. The message names the env var, the file path, and the 1-based certificate position.","triggerScenarios":"The CA-file env var points at a bundle where one block is labeled -----BEGIN CERTIFICATE----- but its base64 payload is corrupt, truncated, or not actually a DER certificate (e.g. a CSR, a public key, or text damaged in copy/paste). The error fires on the first such block; added+1 gives its position among accepted certs.","commonSituations":"Concatenating PEM files with an editor that re-wrapped or truncated lines; a certificate regenerated with the wrong openssl command (e.g. openssl req producing a CSR saved as .crt); files transferred through a channel that mangled base64; bundles built by scripts that append partial output.","solutions":["Inspect the bundle: openssl crl2pkcs7 -nocrl -certfile bundle.pem | openssl pkcs7 -print_certs -noout to find the offending block.","Re-export each CA certificate individually with openssl x509 -outform PEM and re-concatenate them.","Remove non-certificate PEM blocks (CSRs, keys) from the file - only CERTIFICATE blocks belong in the bundle.","Verify the fixed file: every -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- pair must parse (openssl x509 -in bundle.pem -noout succeeds per block)."],"exampleFix":"# before: bundle contains a CSR block mislabeled as a cert\n-----BEGIN CERTIFICATE-----\nMIICgTCCAa... (base64 of a CSR)\n-----END CERTIFICATE-----\n\n# after: re-export the real certificate\nopenssl x509 -in ca.crt -outform PEM >> bundle.pem","handlingStrategy":"validation","validationCode":"// validate a CA bundle before pointing the env var at it\nfunc bundleParses(path string) error {\n    data, err := os.ReadFile(path)\n    if err != nil { return err }\n    n := 0\n    rest := data\n    for {\n        var blk *pem.Block\n        blk, rest = pem.Decode(rest)\n        if blk == nil { break }\n        if blk.Type != \"CERTIFICATE\" { continue }\n        if _, err := x509.ParseCertificate(blk.Bytes); err != nil {\n            return fmt.Errorf(\"certificate %d unparseable: %w\", n+1, err)\n        }\n        n++\n    }\n    if n == 0 { return fmt.Errorf(\"no certificates found\") }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := rootsWithCAFile(path); err != nil {\n    return fmt.Errorf(\"invalid CA bundle %s: %w\", path, err) // abort, never half-trust\n}","preventionTips":["Run openssl crl2pkcs7 -nocrl -certfile bundle.pem | openssl pkcs7 -print_certs -noout in CI for every bundle change.","Generate bundles only by concatenating openssl x509 -outform PEM output.","Never hand-edit PEM files; regenerate from source."],"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"}