AdguardTeam/AdGuardHome · critical

loading tls certificate: %w

Error message

loading tls certificate: %w

What it means

The TLS manager could not parse the configured certificate chain and private key pair into a tls.Certificate via tls.X509KeyPair. The wrapped error is a crypto/tls parse failure: malformed PEM, truncated data, or a key that does not match the certificate.

Source

Thrown at internal/aghtls/defaultmanager.go:477

		mgr.logger.ErrorContext(ctx, "setting tls files", slogutil.KeyError, err)
	}

	mgr.setCertFileTime(ctx)

	return restartHTTPS, nil
}

// updateTLSCert loads and updates a TLS certificate for m.tlsConf.  If
// m.tlsConf is nil, it will be initialized.  extTLSConf must not be nil.  m.mu
// must be locked.
func (mgr *DefaultManager) updateTLSCert(extTLSConf *ExtendedTLSConfig) (err error) {
	if len(extTLSConf.CertificateChainData) == 0 || len(extTLSConf.PrivateKeyData) == 0 {
		return nil
	}

	cert, err := tls.X509KeyPair(extTLSConf.CertificateChainData, extTLSConf.PrivateKeyData)
	if err != nil {
		return fmt.Errorf("loading tls certificate: %w", err)
	}

	slices.Sort(cert.Leaf.DNSNames)

	if mgr.tlsConf == nil {
		mgr.tlsConf = &tls.Config{
			RootCAs:        mgr.rootCerts,
			CipherSuites:   mgr.customCipherIDs,
			MinVersion:     tls.VersionTLS12,
			GetCertificate: mgr.onGetCertificate,
		}
	}

	mgr.tlsCert = &cert

	return nil
}

View on GitHub (pinned to b41aefbe51)

Solutions

  1. Validate the pair externally: openssl x509 -in cert.pem -noout -modulus vs openssl rsa -in key.pem -noout -modulus (or -pubkey comparison for EC)
  2. Confirm both files are PEM (BEGIN CERTIFICATE / BEGIN PRIVATE KEY headers)
  3. If caused by atomic cert replacement, ensure the reload reads files after the rename completes rather than during
  4. Regenerate the certificate/key pair together if they mismatch

Example fix

// before
CertificateChainData: derBytes, // wrong encoding
PrivateKeyData: pemKey,
// after
CertificateChainData: pemCert,
PrivateKeyData: pemKey,
Defensive patterns

Strategy: validation

Validate before calling

if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil {
    return fmt.Errorf("rejecting bad pair before update: %w", err)
}

Try / catch

if err := mgr.SetExtendedTLSConfig(ctx, conf); err != nil {
    // keep serving the previous valid certificate; log and retry next reload
    log.Error("cert reload failed; keeping previous cert", "err", err)
}

Prevention

When it happens

Trigger: updateTLSCert runs on reload or SetExtendedTLSConfig when CertificateChainData/PrivateKeyData are populated but not a valid pair: corrupt PEM blocks, cert signed with a different key, or binary/DER data supplied instead of PEM.

Common situations: Certificates updated on disk mid-read (torn read of a partially written file); mixing up fullchain.pem and privkey.pem; supplying DER instead of PEM; key regenerated after CSR while old cert retained.

Understand the failure class

Related errors


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