Billionmail/BillionMail · error

certificate data is empty

Error message

certificate data is empty

What it means

verifyCertificate is the entry validation for all certificate-install paths (SetSSL, SetSNI, SetPostfixSSL, SetDovecotSSL, SetPostfixVMailCert). It rejects an empty certificate PEM string before any parsing, guarding the rest of the pipeline from nil/empty certificate data.

Source

Thrown at core/internal/service/mail_service/certificate.go:211

	// Update Dovecot configuration
	if err := c.updateDovecotConfig(csrPem, keyPem); err != nil {
		return err
	}

	// Restart Dovecot service
	if err := c.restartDovecot(); err != nil {
		return err
	}

	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)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Confirm the certificate record actually contains PEM data before calling the Set* function
  2. Wait for/retry the ACME issuance and re-fetch the certificate
  3. Check that the correct parameter order (csrPem, keyPem) is used at the call site
  4. Add pre-call validation with a clear user-facing message when cert data is missing

Example fix

// before
cert := getCertFromStore(id)
_ = certService.SetSSL(cert.Cert, cert.Key)
// after
cert := getCertFromStore(id)
if cert == nil || cert.Cert == "" {
    return fmt.Errorf("no certificate available for id %s; issue it first", id)
}
return certService.SetSSL(cert.Cert, cert.Key)
Defensive patterns

Strategy: validation

Validate before calling

func readyToDeploy(certPem, keyPem string) error {
    if strings.TrimSpace(certPem) == "" {
        return errors.New("certificate PEM is empty; issue the certificate first")
    }
    return nil
}
// call before:
// if err := readyToDeploy(cert, key); err != nil { return err }

Type guard

func hasCertificatePEM(s string) bool {
    return strings.Contains(s, "-----BEGIN CERTIFICATE-----")
}

Try / catch

if err := certService.SetSSL(csrPem, keyPem); err != nil {
    if strings.Contains(err.Error(), "certificate data is empty") {
        return fmt.Errorf("certificate for domain not yet issued; run issuance then retry: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling any Set* certificate method with csrPem == "", typically because the certificate was fetched from a store/API and returned empty (issue not yet completed, wrong record ID, or DB field null).

Common situations: Requesting SSL deployment before the ACME order finished; passing the key file content into the cert parameter by mistake; database row deleted between listing and applying.

Understand the failure class

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/6a7c07be72cfde62. Report an issue: GitHub.