{"record":{"id":"64351fb3e90c6220","repo":"Billionmail/BillionMail","slug":"certificate-content-is-empty-in-database","errorCode":null,"errorMessage":"certificate content is empty in database","messagePattern":"certificate content is empty in database","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/certificate.go","lineNumber":513,"sourceCode":"\t\tNotAfter    string `json:\"not_after\"`\n\t\tNotBefore   string `json:\"not_before\"`\n\t\tDns         string `json:\"dns\"`\n\t}\n\n\terr = g.DB().Model(\"letsencrypts\").\n\t\tWhere(\"dns::jsonb ? $1\", public.FormatMX(domain)).\n\t\tWhere(\"status = 1\").\n\t\tWhere(\"endtime > ?\", time.Now().Unix()).\n\t\tOrder(\"endtime desc\").\n\t\tLimit(1).\n\t\tScan(&cert)\n\n\tif err != nil {\n\t\treturn certInfo, fmt.Errorf(\"certificate not found in database: %v\", err)\n\t}\n\n\tif cert.Certificate == \"\" {\n\t\treturn certInfo, fmt.Errorf(\"certificate content is empty in database\")\n\t}\n\n\t// Parse certificate information\n\terr = gconv.Struct(acme.GetCertInfo(cert.Certificate), &certInfo)\n\tif err != nil {\n\t\treturn certInfo, fmt.Errorf(\"failed to parse certificate info: %v\", err)\n\t}\n\n\t// Set certificate content\n\tcertInfo.CertPem = cert.Certificate\n\tcertInfo.KeyPem = cert.PrivateKey\n\n\treturn certInfo, nil\n}\n\n// getSSLInfoFromFiles retrieves SSL certificate from file system (legacy method)\nfunc (c *Certificate) getSSLInfoFromFiles(domain string) (certInfo v1.CertInfo, err error) {\n\tcsrPath := filepath.Join(consts.SSL_PATH, domain, \"/fullchain.pem\")","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/certificate.go#L495-L531","documentation":"After a certificate row is found, getSSLInfoFromDatabase checks cert.Certificate is non-empty. An empty PEM blob means the row exists but the certificate content column was never populated or was cleared, so there is nothing to parse or serve.","triggerScenarios":"GetSSLInfo reads a DB row where the certificate column is '' — e.g. a record created by a failed issuance, manual row insertion, or a wiped/placeholder row.","commonSituations":"Issuance job crashed after inserting the row but before writing PEM content; admin deleted cert content while debugging; backup/restore truncated large text columns.","solutions":["Re-issue or re-upload the certificate so the certificate column contains valid PEM.","Delete the empty row so the service falls back to a valid certificate or triggers issuance.","Check the issuance pipeline for steps that persist the row before the PEM is available.","Inspect the row directly (SELECT certificate FROM ... ) to confirm emptiness."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"var pem string\nerr := g.DB().Model(\"certificates\").Where(\"endtime > ?\", time.Now().Unix()).Order(\"endtime desc\").Limit(1).Value(\"certificate\").Scan(&pem)\nif err != nil || !strings.Contains(pem, \"BEGIN CERTIFICATE\") {\n    // row empty or invalid — re-issue before calling GetSSLInfo\n}","typeGuard":null,"tryCatchPattern":"if err := GetSSLStatus(ctx); err != nil {\n    if strings.Contains(err.Error(), \"certificate content is empty\") {\n        return reissueCertificate(ctx)\n    }\n    return err\n}","preventionTips":["Never insert certificate rows manually with placeholder content.","Make issuance transactions atomic: write PEM in the same transaction as the row.","Add a health check that flags cert rows with empty content."],"tags":["ssl","certificate","database","data-integrity"],"backgroundTag":"empty-certificate-content","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}