{"record":{"id":"7e5204865f5a2e2e","repo":"AdguardTeam/AdGuardHome","slug":"loading-tls-certificate-w","errorCode":null,"errorMessage":"loading tls certificate: %w","messagePattern":"loading tls certificate: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/aghtls/defaultmanager.go","lineNumber":477,"sourceCode":"\t\tmgr.logger.ErrorContext(ctx, \"setting tls files\", slogutil.KeyError, err)\n\t}\n\n\tmgr.setCertFileTime(ctx)\n\n\treturn restartHTTPS, nil\n}\n\n// updateTLSCert loads and updates a TLS certificate for m.tlsConf.  If\n// m.tlsConf is nil, it will be initialized.  extTLSConf must not be nil.  m.mu\n// must be locked.\nfunc (mgr *DefaultManager) updateTLSCert(extTLSConf *ExtendedTLSConfig) (err error) {\n\tif len(extTLSConf.CertificateChainData) == 0 || len(extTLSConf.PrivateKeyData) == 0 {\n\t\treturn nil\n\t}\n\n\tcert, err := tls.X509KeyPair(extTLSConf.CertificateChainData, extTLSConf.PrivateKeyData)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"loading tls certificate: %w\", err)\n\t}\n\n\tslices.Sort(cert.Leaf.DNSNames)\n\n\tif mgr.tlsConf == nil {\n\t\tmgr.tlsConf = &tls.Config{\n\t\t\tRootCAs:        mgr.rootCerts,\n\t\t\tCipherSuites:   mgr.customCipherIDs,\n\t\t\tMinVersion:     tls.VersionTLS12,\n\t\t\tGetCertificate: mgr.onGetCertificate,\n\t\t}\n\t}\n\n\tmgr.tlsCert = &cert\n\n\treturn nil\n}\n","sourceCodeStart":459,"sourceCodeEnd":495,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/aghtls/defaultmanager.go#L459-L495","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate the pair externally: openssl x509 -in cert.pem -noout -modulus vs openssl rsa -in key.pem -noout -modulus (or -pubkey comparison for EC)","Confirm both files are PEM (BEGIN CERTIFICATE / BEGIN PRIVATE KEY headers)","If caused by atomic cert replacement, ensure the reload reads files after the rename completes rather than during","Regenerate the certificate/key pair together if they mismatch"],"exampleFix":"// before\nCertificateChainData: derBytes, // wrong encoding\nPrivateKeyData: pemKey,\n// after\nCertificateChainData: pemCert,\nPrivateKeyData: pemKey,","handlingStrategy":"validation","validationCode":"if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil {\n    return fmt.Errorf(\"rejecting bad pair before update: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := mgr.SetExtendedTLSConfig(ctx, conf); err != nil {\n    // keep serving the previous valid certificate; log and retry next reload\n    log.Error(\"cert reload failed; keeping previous cert\", \"err\", err)\n}","preventionTips":["Validate PEM data with tls.X509KeyPair before assigning it to the manager","Read cert files atomically (snapshot both files after rename)","Alert on renewal pipelines so mismatched pairs are caught early"],"tags":["tls","x509","pem","certificate","crypto"],"backgroundTag":"certificate-key-mismatch","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}