JuliusBrussee/caveman · error

: trailing PEM block is truncated after certificate(s), so…

Error message

%s: trailing PEM block is truncated after %d certificate(s), so the bundle is incomplete and must not be half-trusted

What it means

After decoding all complete PEM blocks, Certificates checks whether the remaining bytes still contain '-----BEGIN'. If so, the file ends with a truncated PEM block, and the library refuses to return the certificates parsed so far — a half-trusted bundle is treated as a security risk. The error reports the file path and how many complete certificates were parsed.

Solutions

  1. Compare the file against the source bundle: check `openssl crl2pkcs7 -nocrl -certfile bundle.pem | openssl pkcs7 -print_certs` lists every expected certificate, and re-copy/re-download the full file.
  2. Look at the end of the file (`tail -5 bundle.pem`); ensure it ends with '-----END CERTIFICATE-----' and complete base64.
  3. If copying certificates manually, paste each block fully including BEGIN/END lines, one certificate at a time.
  4. Fix the pipeline producing the file (download, secret manifest, build step) so writes complete atomically (write to temp then rename).

Example fix

// before (truncated bundle)
cat root.pem partial-intermediate.pem > bundle.pem // last block cut off
// after
cat root.pem full-intermediate.pem > bundle.pem
tail -1 bundle.pem # => -----END CERTIFICATE-----
Defensive patterns

Strategy: validation

Validate before calling

func bundleComplete(path string) error {
    data, err := os.ReadFile(path)
    if err != nil { return err }
    if bytes.Contains(data, []byte("-----BEGIN")) && !bytes.Contains(data, []byte("-----END CERTIFICATE-----")) {
        return fmt.Errorf("%s ends with a truncated PEM block", path)
    }
    return nil
}
// call before cabundle.Certificates

Try / catch

certs, err := cabundle.Certificates(path)
if err != nil && strings.Contains(err.Error(), "truncated") {
    return fmt.Errorf("TLS bundle %s is incomplete; re-copy the full chain: %w", path, err)
}

Prevention

When it happens

Trigger: Calling Certificates (or Pool/loadRootCAs) on a PEM file whose final certificate block was cut off mid-base64 or lacks its END line — typically from a partial file write, a truncated mount/copy, or a copy-paste that dropped the tail.

Common situations: Kubernetes secret truncated by a size/formatting mistake; an incomplete docker build COPY of a partially downloaded bundle; editors or tools that clipped a long pasted chain; interrupted file download of a CA bundle.

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.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/3b9743effbb4b539. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/cabundle/cabundle.go:74

	var certs []*x509.Certificate
	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: 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)