{"record":{"id":"6a7c07be72cfde62","repo":"Billionmail/BillionMail","slug":"certificate-data-is-empty","errorCode":null,"errorMessage":"certificate data is empty","messagePattern":"certificate data is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/certificate.go","lineNumber":211,"sourceCode":"\n\t// Update Dovecot configuration\n\tif err := c.updateDovecotConfig(csrPem, keyPem); err != nil {\n\t\treturn err\n\t}\n\n\t// Restart Dovecot service\n\tif err := c.restartDovecot(); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// verifyCertificate validates certificate data\nfunc (c *Certificate) verifyCertificate(csrPem, keyPem string) error {\n\t// Check if certificate data is empty\n\tif csrPem == \"\" {\n\t\treturn fmt.Errorf(\"certificate data is empty\")\n\t}\n\tif keyPem == \"\" {\n\t\treturn fmt.Errorf(\"private key data is empty\")\n\t}\n\n\t// Validate certificate\n\tcInfo := acme.GetCertInfo(csrPem)\n\n\tif cInfo.Endtime == 0 {\n\t\treturn fmt.Errorf(\"certificate is invalid\")\n\t}\n\n\treturn nil\n}\n\n// updatePostfixConfig updates Postfix configuration with new certificate\nfunc (c *Certificate) updatePostfixConfig(csrPem, keyPem string) error {\n\tmainCf := public.AbsPath(consts.POSTFIX_MAIN_CONF)","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/certificate.go#L193-L229","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Confirm the certificate record actually contains PEM data before calling the Set* function","Wait for/retry the ACME issuance and re-fetch the certificate","Check that the correct parameter order (csrPem, keyPem) is used at the call site","Add pre-call validation with a clear user-facing message when cert data is missing"],"exampleFix":"// before\ncert := getCertFromStore(id)\n_ = certService.SetSSL(cert.Cert, cert.Key)\n// after\ncert := getCertFromStore(id)\nif cert == nil || cert.Cert == \"\" {\n    return fmt.Errorf(\"no certificate available for id %s; issue it first\", id)\n}\nreturn certService.SetSSL(cert.Cert, cert.Key)","handlingStrategy":"validation","validationCode":"func readyToDeploy(certPem, keyPem string) error {\n    if strings.TrimSpace(certPem) == \"\" {\n        return errors.New(\"certificate PEM is empty; issue the certificate first\")\n    }\n    return nil\n}\n// call before:\n// if err := readyToDeploy(cert, key); err != nil { return err }","typeGuard":"func hasCertificatePEM(s string) bool {\n    return strings.Contains(s, \"-----BEGIN CERTIFICATE-----\")\n}","tryCatchPattern":"if err := certService.SetSSL(csrPem, keyPem); err != nil {\n    if strings.Contains(err.Error(), \"certificate data is empty\") {\n        return fmt.Errorf(\"certificate for domain not yet issued; run issuance then retry: %w\", err)\n    }\n    return err\n}","preventionTips":["Only invoke Set* after confirming the ACME order state is 'issued'","Store cert and key in separate, non-nullable columns and check both on load","Log the cert record ID when the PEM is empty to speed diagnosis"],"tags":["tls","certificate","validation","go"],"backgroundTag":"empty-certificate-data","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}