JuliusBrussee/caveman · error
%s (%s): certificate %d is unparseable, so the bundle is inc
Error message
%s (%s): certificate %d is unparseable, so the bundle is incomplete and must not be half-trusted: %w
What it means
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.
Source
Thrown at shared/platform/chhttp/chhttp.go:182
}
roots, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("%s: load system certificate pool: %w", caFileEnv, err)
}
added := 0
rest := bundle
for {
var block *pem.Block
block, rest = pem.Decode(rest)
if block == nil {
break
}
if block.Type != "CERTIFICATE" {
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return 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)
}
roots.AddCert(cert)
added++
}
if bytes.Contains(rest, []byte("-----BEGIN")) {
return 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)
}
if added == 0 {
return nil, fmt.Errorf("%s (%s) contains no valid PEM certificate", caFileEnv, path)
}
return roots, nil
}
// errTransport fails every request with the configuration error that produced
// it. A client constructor cannot return an error, and falling back to the
// default transport would silently trade a rejected TLS configuration for
// unpinned verification — so the client is built, and refuses to send.
type errTransport struct{ err error }View on GitHub (pinned to 27d5a3981a)
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).
Example fix
# before: bundle contains a CSR block mislabeled as a cert -----BEGIN CERTIFICATE----- MIICgTCCAa... (base64 of a CSR) -----END CERTIFICATE----- # after: re-export the real certificate openssl x509 -in ca.crt -outform PEM >> bundle.pem
Defensive patterns
Strategy: validation
Validate before calling
// validate a CA bundle before pointing the env var at it
func bundleParses(path string) error {
data, err := os.ReadFile(path)
if err != nil { return err }
n := 0
rest := data
for {
var blk *pem.Block
blk, rest = pem.Decode(rest)
if blk == nil { break }
if blk.Type != "CERTIFICATE" { continue }
if _, err := x509.ParseCertificate(blk.Bytes); err != nil {
return fmt.Errorf("certificate %d unparseable: %w", n+1, err)
}
n++
}
if n == 0 { return fmt.Errorf("no certificates found") }
return nil
} Try / catch
if _, err := rootsWithCAFile(path); err != nil {
return fmt.Errorf("invalid CA bundle %s: %w", path, err) // abort, never half-trust
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- %s (%s) contains no valid PEM certificate
- %s: load system certificate pool: %w
- %s (%s): trailing PEM block is truncated after %d certificat
- production requires an https:// CLICKHOUSE_URL (TLS only); C
- invalid listen address %q: %w
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/2ac9fe0041833a4e.
Report an issue: GitHub.