{"record":{"id":"a88e5c18374e3e0b","repo":"AdguardTeam/AdGuardHome","slug":"certificate-does-not-verify-w","errorCode":null,"errorMessage":"certificate does not verify: %w","messagePattern":"certificate does not verify: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/aghtls/defaultmanager.go","lineNumber":711,"sourceCode":"\t}\n\n\tothersLen := len(others)\n\tif othersLen > 0 {\n\t\tlogger.InfoContext(\n\t\t\tctx,\n\t\t\t\"verifying certificate chain: got an intermediate cert\",\n\t\t\t\"num\", othersLen,\n\t\t)\n\t}\n\n\topts := x509.VerifyOptions{\n\t\tDNSName:       srvName,\n\t\tRoots:         rootCAs,\n\t\tIntermediates: pool,\n\t}\n\t_, err = main.Verify(opts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"certificate does not verify: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// errNoIPInCert is the error that is returned from [parseCertChain]\n// if the leaf certificate doesn't contain IPs.\nconst errNoIPInCert errors.Error = `certificates has no IP addresses; ` +\n\t`DNS-over-TLS won't be advertised via DDR`\n\n// parseCertChain parses the certificate chain from raw data, and returns it.\n// If ok is true, the returned error, if any, is not critical.  logger must not\n// be nil.\nfunc parseCertChain(\n\tctx context.Context,\n\tlogger *slog.Logger,\n\tchain []byte,\n) (parsedCerts []*x509.Certificate, ok bool, err error) {","sourceCodeStart":693,"sourceCodeEnd":729,"githubUrl":"https://github.com/AdguardTeam/AdGuardHome/blob/b41aefbe51c8dde65e2c50f093996afa0502edf9/internal/aghtls/defaultmanager.go#L693-L729","documentation":"x509 certificate verification failed while validating the certificate chain. The main certificate was checked against the provided (or system) root CAs and optional intermediates, and Verify rejected it: expired cert, unknown authority, hostname mismatch, or constraint violations.","triggerScenarios":"validateCertChain runs when the configured chain is validated with srvName and rootCAs; triggers include an expired certificate, a self-signed cert without its CA in the trust pool, a chain missing intermediates, or DNSName not matching the cert's SANs.","commonSituations":"Let's Encrypt cert not renewed before the 90-day expiry; corporate self-signed CA not added to the trusted roots; serving an internal hostname not present in the certificate SANs; incomplete chain (leaf only, no intermediates).","solutions":["Check expiry: openssl x509 -in cert.pem -noout -dates and renew if expired","Verify the chain: openssl verify -CAfile ca.pem -untrusted intermediates.pem cert.pem","Ensure the SANs include the DNS name being validated (srvName)","Add the signing CA to the trusted root pool used for validation"],"exampleFix":"// before\nroots absent / self-signed leaf only\n// after\nrootCAs.AppendCertsFromPEM(selfSignedCAPEM)","handlingStrategy":"validation","validationCode":"leaf, _ := x509.ParseCertificate(certDER)\nif _, err := leaf.Verify(x509.VerifyOptions{DNSName: srvName, Roots: rootCAs, Intermediates: pool}); err != nil {\n    return fmt.Errorf(\"pre-flight verify failed: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := mgr.LoadTLSConfig(ctx, conf); err != nil {\n    var x509Err x509.CertificateInvalidError\n    if errors.As(err, &x509Err) { /* expiry, hostname, constraints */ }\n}","preventionTips":["Automate certificate renewal well before expiry","Include full intermediate chain in the configured file","Keep self-signed CAs in the trust pool used for validation"],"tags":["tls","x509","certificate","verification","chain"],"backgroundTag":"x509-certificate-verification-failed","analyzedSha":"b41aefbe51c8dde65e2c50f093996afa0502edf9","analyzedAt":"2026-08-27T04:57:55.097Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}