AdguardTeam/AdGuardHome · critical

parsing tls certificate: %w

Error message

parsing tls certificate: %w

What it means

prepareTLSConfig (used by NewDefaultManager) loads and parses the configured TLS certificate with tls.X509KeyPair-style parsing; failure here means the certificate or key material couldn't be parsed — malformed PEM, missing blocks, or key/cert mismatch. External TLS is disabled as a side effect (mgr.extTLSConf.Enabled = false).

Source

Thrown at internal/aghtls/defaultmanager.go:163

		mgr.extTLSConf.Enabled = false

		// Don't wrap the error, because it's informative enough as is.
		return err
	}

	cert, err := tls.X509KeyPair(mgr.extTLSConf.CertificateChainData, mgr.extTLSConf.PrivateKeyData)
	if err != nil {
		// DNSCrypt provides its own certificate, meaning we can ignore TLS
		// certificate parsing errors.
		if mgr.extTLSConf.PortDNSCrypt != 0 && mgr.extTLSConf.DNSCryptConfigFile != "" {
			mgr.logger.InfoContext(ctx, "dnscrypt is configured")

			return nil
		}

		mgr.extTLSConf.Enabled = false

		return fmt.Errorf("parsing tls certificate: %w", err)
	}

	slices.Sort(cert.Leaf.DNSNames)

	mgr.tlsCert = &cert
	mgr.setCertFileTime(ctx)
	mgr.tlsConf = &tls.Config{
		RootCAs:        mgr.rootCerts,
		CipherSuites:   mgr.customCipherIDs,
		GetCertificate: mgr.onGetCertificate,
		MinVersion:     tls.VersionTLS12,
	}

	return nil
}

// type check
var _ Manager = (*DefaultManager)(nil)

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Validate the pair offline: openssl x509 -in cert.pem -noout and openssl rsa -in key.pem -check
  2. Confirm cert and key match: compare openssl x509 -noout -modulus | openssl md5 for both (or pkey for RSA alternatives)
  3. Re-export/re-issue the pair as unencrypted PEM and update the config paths
  4. After fixing, restart so NewDefaultManager re-attempts loading

Example fix

# verify match
openssl x509 -in cert.pem -noout -modulus | openssl md5
openssl rsa -in key.pem -noout -modulus | openssl md5
Defensive patterns

Strategy: validation

Validate before calling

// verify pair parses and matches before starting the manager:
if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil { return fmt.Errorf("invalid pair: %w", err) }

Type guard

func certMatchesKey(certPEM, keyPEM []byte) bool { _, err := tls.X509KeyPair(certPEM, keyPEM); return err == nil }

Try / catch

mgr, err := aghtls.NewDefaultManager(...)
if err != nil && strings.Contains(err.Error(), "parsing tls certificate") {
    // fail startup, keep HTTP-only, alert operator; external TLS is disabled by this point
}

Prevention

When it happens

Trigger: NewDefaultManager with a certificate file or key that isn't valid PEM, where the key doesn't match the certificate, where either file is empty/truncated, or where the key is encrypted and can't be decoded; also file read issues upstream surface wrapped here.

Common situations: Expired-then-regenerated certs pasted with truncated headers; cert and key from different issuance rounds; wrong file paths yielding empty content; keys in PKCS#12 or unusual formats instead of PEM.

Understand the failure class

Related errors


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