caddyserver/caddy · error

parsing certificate at index %d: %v

Error message

parsing certificate at index %d: %v

What it means

InlineCAPool.Provision decodes each entry of trusted_ca_certs (base64-encoded DER certificates) with decodeBase64DERCert. When an entry is not valid base64, not DER, or not an X.509 certificate, provisioning of the client CA pool fails, reporting the failing index so you can locate the bad entry.

Source

Thrown at modules/caddytls/capools.go:77

// CaddyModule implements caddy.Module.
func (icp InlineCAPool) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID: "tls.ca_pool.source.inline",
		New: func() caddy.Module {
			return new(InlineCAPool)
		},
	}
}

// Provision implements caddy.Provisioner.
func (icp *InlineCAPool) Provision(ctx caddy.Context) error {
	caPool := x509.NewCertPool()
	var certs []*x509.Certificate
	for i, clientCAString := range icp.TrustedCACerts {
		clientCA, err := decodeBase64DERCert(clientCAString)
		if err != nil {
			return fmt.Errorf("parsing certificate at index %d: %v", i, err)
		}
		caPool.AddCert(clientCA)
		certs = append(certs, clientCA)
	}
	icp.pool = caPool
	icp.certs = certs

	return nil
}

// Syntax:
//
//	trust_pool inline {
//		trust_der <base64_der_cert>...
//	}
//
// The 'trust_der' directive can be specified multiple times.
func (icp *InlineCAPool) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Convert the PEM to base64 DER: openssl x509 -in ca.pem -outform der | base64 -w0, then use that string.
  2. Check the reported index against your trusted_ca_certs array to find the exact bad entry.
  3. Alternatively use trusted_ca_certs_pem_files (PEM file path) which avoids the conversion entirely.
  4. Verify the string is a single unbroken standard-alphabet base64 blob with no stray characters.

Example fix

# before (PEM text stuffed into the base64-DER field)
"trusted_ca_certs": ["-----BEGIN CERTIFICATE-----\nMIIF..."]

# after (base64 DER)
openssl x509 -in ca.pem -outform der | base64 -w0
"trusted_ca_certs": ["MIIFazCCA...="]
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check each inline cert exactly as Caddy does.
for i, s := range poolCfg.TrustedCACerts {
    if _, err := decodeBase64DERCert(s); err != nil { // or inline: base64 decode + x509.ParseCertificate
        return fmt.Errorf("trusted_ca_certs[%d] invalid: %v", i, err)
    }
}

Type guard

func isBase64DERCert(s string) bool {
    der, err := base64.StdEncoding.DecodeString(strings.TrimSpace(s))
    if err != nil {
        return false
    }
    _, err = x509.ParseCertificate(der)
    return err == nil
}

Prevention

When it happens

Trigger: Configuring client_auth.trusted_ca_certs (or the Caddyfile trusted_ca_cert_file's inline sibling) with a value that is a PEM string instead of base64 DER, has trailing whitespace/newline corruption, uses URL-safe instead of standard base64, or is a truncated copy-paste.

Common situations: Copy-pasting a PEM block (-----BEGIN CERTIFICATE-----) into a field that expects bare base64 DER; line-wrapped base64 pasted with literal '\n' characters; using the certificate's fingerprint instead of the certificate.

Understand the failure class

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/2d8ca0182f128099. Report an issue: GitHub.