Billionmail/BillionMail · error
certificate is invalid
Error message
certificate is invalid
What it means
After the emptiness checks, verifyCertificate parses the certificate with acme.GetCertInfo and requires cInfo.Endtime != 0. Endtime == 0 means the PEM could not be parsed as a valid X.509 certificate, so it is reported as 'certificate is invalid'.
Source
Thrown at core/internal/service/mail_service/certificate.go:221
return nil
}
// verifyCertificate validates certificate data
func (c *Certificate) verifyCertificate(csrPem, keyPem string) error {
// Check if certificate data is empty
if csrPem == "" {
return fmt.Errorf("certificate data is empty")
}
if keyPem == "" {
return fmt.Errorf("private key data is empty")
}
// Validate certificate
cInfo := acme.GetCertInfo(csrPem)
if cInfo.Endtime == 0 {
return fmt.Errorf("certificate is invalid")
}
return nil
}
// updatePostfixConfig updates Postfix configuration with new certificate
func (c *Certificate) updatePostfixConfig(csrPem, keyPem string) error {
mainCf := public.AbsPath(consts.POSTFIX_MAIN_CONF)
content, err := os.ReadFile(mainCf)
if err != nil {
return fmt.Errorf("failed to read postfix config: %v", err)
}
// Write certificate and key to files
certPath := public.AbsPath(filepath.Join(consts.SSL_PATH, "postfix.crt"))
keyPath := public.AbsPath(filepath.Join(consts.SSL_PATH, "postfix.key"))
if err := os.WriteFile(certPath, []byte(csrPem), 0755); err != nil {View on GitHub (pinned to fc36c76c05)
Solutions
- Validate the PEM with openssl x509 -in cert.pem -noout before deploying
- Confirm the value is the issued certificate, not the CSR or key
- Re-issue/download the certificate from the ACME provider
- Check for content corruption in storage (encoding, truncation, escaping)
Defensive patterns
Strategy: validation
Validate before calling
func certParses(pemStr string) error {
blk, _ := pem.Decode([]byte(pemStr))
if blk == nil || blk.Type != "CERTIFICATE" {
return errors.New("not a CERTIFICATE PEM block")
}
_, err := x509.ParseCertificate(blk.Bytes)
return err
} Type guard
func isValidCertPEM(s string) bool {
blk, _ := pem.Decode([]byte(s))
if blk == nil || blk.Type != "CERTIFICATE" { return false }
_, err := x509.ParseCertificate(blk.Bytes)
return err == nil
} Try / catch
if err := certService.SetSSL(csrPem, keyPem); err != nil {
if strings.Contains(err.Error(), "certificate is invalid") {
log.Errorf("PEM rejected by x509 parse; first 60 chars: %.60s", csrPem)
return ErrBadCertificatePEM
}
return err
} Prevention
- Store only the issued certificate, never the CSR or ACME response page
- Run openssl x509 -noout -enddate as a pre-deploy sanity check
- Avoid transformations (trim/escape) that corrupt base64 when persisting PEM
When it happens
Trigger: csrPem is non-empty but not a parseable certificate: truncated PEM, wrong content (e.g. a CSR, a private key, or HTML/error page) passed as the cert, or malformed base64 in the PEM body.
Common situations: Saving an ACME error page instead of the issued cert; confusing CSR with certificate; PEM with altered line breaks/base64 corruption in a database text column.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- certificate data is empty
- private key data is empty
- invalid certificate
- required column 'email' not found
- fail to set ssl: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/94840a23ee5669cd.
Report an issue: GitHub.