{"record":{"id":"d12bc01389e6e6e0","repo":"Billionmail/BillionMail","slug":"invalid-certificate","errorCode":null,"errorMessage":"invalid certificate","messagePattern":"invalid certificate","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/acme/cli.go","lineNumber":227,"sourceCode":"\tif err != nil {\n\t\treturn \"\", \"\", fmt.Errorf(\"failed to save private key: %v\", err)\n\t}\n\n\tfmt.Printf(\"Certificate saved to: %s\\n\", certificatePath)\n\tfmt.Printf(\"Private key saved to: %s\\n\", privateKeyPath)\n\n\treturn certificatePath, privateKeyPath, nil\n}\n\n/**\n * @brief Save certificate to database\n * @return error\n */\nfunc (cli *AcmeCLI) SaveToDatabase(accountId int, certificate, privateKey string) (int, error) {\n\t// Get certificate info\n\tcertInfo := GetCertInfo(certificate)\n\tif certInfo.Subject == \"\" {\n\t\treturn 0, fmt.Errorf(\"invalid certificate\")\n\t}\n\n\t// Prepare DNS names\n\tdnsNames, _ := json.Marshal(cli.Domains)\n\n\t// Prepare data\n\tdata := map[string]interface{}{\n\t\t\"account_id\":   accountId,\n\t\t\"certificate\":  certificate,\n\t\t\"private_key\":  privateKey,\n\t\t\"subject\":      certInfo.Subject,\n\t\t\"dns\":          string(dnsNames),\n\t\t\"not_before\":   certInfo.NotBefore,\n\t\t\"not_after\":    certInfo.NotAfter,\n\t\t\"endtime\":      certInfo.Endtime,\n\t\t\"issuer\":       certInfo.Issuer,\n\t\t\"auth_type\":    cli.VerifyType,\n\t\t\"dns_provider\": cli.DnsProvider,","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/acme/cli.go#L209-L245","documentation":"SaveToDatabase parses the issued certificate with GetCertInfo and rejects it when the Subject field comes back empty, meaning the certificate PEM could not be parsed or is empty. This guards against persisting a malformed/blank certificate record. It indicates the certificate string passed in is not a valid PEM certificate.","triggerScenarios":"Calling SaveToDatabase with an empty certificate string, a certificate path instead of PEM content, or PEM that GetCertInfo cannot parse (truncated file, wrong file passed, previous Apply step partially failed).","commonSituations":"Passing a file path where PEM content is expected; the certificate file was saved empty due to an earlier write error; certificate chain file corrupted during transfer; calling SaveToDatabase before a successful Apply.","solutions":["Verify the certificate argument contains actual PEM content (-----BEGIN CERTIFICATE-----) not a file path","Check GetCertInfo against the same certificate to see why Subject is empty","Re-run Apply and confirm the certificate file is non-empty before saving","Validate the certificate with openssl x509 -in cert.pem -noout -subject"],"exampleFix":"// before\nerr := cli.SaveToDatabase(accountId, certPath, keyPath)\n// after\ncertPEM, _ := os.ReadFile(certPath)\nif len(certPEM) == 0 { return errors.New(\"certificate file is empty\") }\nerr := cli.SaveToDatabase(accountId, string(certPEM), keyPEM)","handlingStrategy":"validation","validationCode":"certPEM, err := os.ReadFile(certPath)\nif err != nil || !bytes.Contains(certPEM, []byte(\"-----BEGIN CERTIFICATE-----\")) {\n    return errors.New(\"certificate PEM missing or malformed\")\n}\nif acme.GetCertInfo(string(certPEM)).Subject == \"\" {\n    return errors.New(\"certificate not parseable — refusing to save\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass PEM content, never a file path, to SaveToDatabase","Validate with openssl x509 before persisting","Confirm Apply returned non-empty paths and files are non-empty first"],"tags":["acme","certificate","validation","pem"],"backgroundTag":"invalid-pem-certificate","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"}