AdguardTeam/AdGuardHome · error

parsing certificate at index %d: %w

Error message

parsing certificate at index %d: %w

What it means

A PEM block in the certificate chain could not be parsed as an X.509 certificate. parsePEMCerts iterates all PEM blocks and x509.ParseCertificate failed on block index i — the block exists but its DER payload is corrupt or is not actually a certificate.

Source

Thrown at internal/aghtls/defaultmanager.go:761

		return nil, false, err
	}

	logger.InfoContext(ctx, "parsing multiple pem certificates", "num", len(parsedCerts))

	if !CertificateHasIP(parsedCerts[0]) {
		err = errNoIPInCert
	}

	return parsedCerts, true, err
}

// parsePEMCerts parses multiple PEM-encoded certificates.
func parsePEMCerts(certs []*pem.Block) (parsedCerts []*x509.Certificate, err error) {
	for i, cert := range certs {
		var parsed *x509.Certificate
		parsed, err = x509.ParseCertificate(cert.Bytes)
		if err != nil {
			return nil, fmt.Errorf("parsing certificate at index %d: %w", i, err)
		}

		parsedCerts = append(parsedCerts, parsed)
	}

	if len(parsedCerts) == 0 {
		return nil, errors.Error("empty certificate")
	}

	return parsedCerts, nil
}

// validatePKey validates the private key, returning its type.  It returns an
// empty string if error occurs.
func validatePKey(pkey []byte) (keyType string, err error) {
	var key *pem.Block

	// Go through all pem blocks, but take first valid pem block and drop the

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Regenerate or re-download the certificate chain from the issuer
  2. Validate offline: openssl x509 -in chain.pem -noout for each block to find the corrupt one
  3. If caused by partial writes, ensure the file producer writes atomically (write temp + rename)
  4. Remove non-certificate PEM blocks (keys, CSRs) from the chain file

Example fix

// before
# chain.pem contains a stray PRIVATE KEY block
// after
# chain.pem contains only CERTIFICATE blocks, leaf first
Defensive patterns

Strategy: validation

Validate before calling

rest := pemData
for {
    block, rest := pem.Decode(rest)
    if block == nil { break }
    if block.Type != "CERTIFICATE" { return fmt.Errorf("non-certificate block: %s", block.Type) }
    if _, err := x509.ParseCertificate(block.Bytes); err != nil {
        return fmt.Errorf("bad block: %w", err)
    }
}

Try / catch

if err := mgr.LoadTLSConfig(ctx, conf); err != nil {
    // surface index from message; isolate and re-download the offending cert
}

Prevention

When it happens

Trigger: A PEM block with a CERTIFICATE-type header but garbage/truncated DER content; a PEM block that is actually a key or CSR mislabeled; base64 corruption inside the PEM payload.

Common situations: Hand-edited or copy-pasted certificate files with truncated lines; concatenating files in the wrong order with stray headers; cert file partially written during atomic replacement.

Understand the failure class

Related errors


AI-assisted analysis of AdguardTeam/AdGuardHome@b41aefbe51 (2026-08-27). Data as JSON: /api/errors/2d2c1193ce95a6aa. Report an issue: GitHub.