JuliusBrussee/caveman · error
contains no valid PEM certificate
Error message
%s contains no valid PEM certificate
What it means
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.
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.
Example fix
// before
pool, err := NewPool(WithBundle("./notes.txt")) // notes.txt has no PEM
// after
pool, err := NewPool(WithBundle("/etc/ssl/certs/ca-certificates.crt")) Defensive patterns
Strategy: validation
Validate before calling
func hasPEM(path string) bool {
b, err := os.ReadFile(path)
return err == nil && bytes.Contains(b, []byte("-----BEGIN CERTIFICATE-----"))
} Try / catch
certs, err := loadCertificates(path)
if err != nil {
return fmt.Errorf("CA bundle unusable, refusing to start: %w", err)
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- : certificate is unparseable, so the bundle is incomplete…
- : trailing PEM block is truncated after certificate(s), so…
- ca_bundle
- ca bundle
- postgres: CA certificate configured while TLS is disabled
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/42dcf1b9072137c6.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/cabundle/cabundle.go:77
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: certificate %d is unparseable, so the bundle is incomplete and must not be half-trusted: %w", path, len(certs)+1, err)
}
certs = append(certs, cert)
}
if bytes.Contains(rest, []byte("-----BEGIN")) {
return 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))
}
if len(certs) == 0 {
return nil, fmt.Errorf("%s contains no valid PEM certificate", path)
}
return certs, nil
}
View on GitHub (pinned to 3ee70a1026)