Billionmail/BillionMail · error

private key data is empty

Error message

private key data is empty

What it means

Validation guard in verifyCertificate: the keyPem argument (TLS private key PEM) is an empty string, so the certificate/key pair cannot be validated before being installed into Postfix/Dovecot. verifyCertificate rejects it before calling acme.GetCertInfo, since cert data without a key is unusable for TLS.

Source

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

		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)
	content, err := os.ReadFile(mainCf)
	if err != nil {
		return fmt.Errorf("failed to read postfix config: %v", err)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Ensure the private key PEM is present and passed as the second argument
  2. Re-export or regenerate the key pair if the key is truly lost
  3. Verify field mapping when loading cert/key from the database or API response
  4. Validate inputs before the call the same way verifyCertificate does

Example fix

// before
if err := c.SetSSL(certPem, ""); err != nil { ... }
// after
if keyPem == "" {
    return errors.New("private key PEM is required to deploy the certificate")
}
if err := c.SetSSL(certPem, keyPem); err != nil { ... }
Defensive patterns

Strategy: validation

Validate before calling

func readyToDeploy(certPem, keyPem string) error {
    if strings.TrimSpace(keyPem) == "" {
        return errors.New("private key PEM is empty; regenerate or re-export the key")
    }
    return nil
}

Type guard

func hasPrivateKeyPEM(s string) bool {
    return strings.Contains(s, "-----BEGIN ") && strings.Contains(s, "PRIVATE KEY-----")
}

Try / catch

if err := certService.SetSSL(csrPem, keyPem); err != nil {
    if strings.Contains(err.Error(), "private key data is empty") {
        return fmt.Errorf("missing key for certificate; re-export key pair: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling SetSSL/SetSNI/SetPostfixSSL/SetDovecotSSL/SetPostfixVMailCert with keyPem == "" — key not yet generated, wrong field read from the store, or key lost during export.

Common situations: ACME flow that stored only the cert; migrating certificates and losing the .key file; copy/paste omitting the key PEM block.

Related errors


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