{"record":{"id":"d0b35cdb7c0e6c66","repo":"Billionmail/BillionMail","slug":"fail-to-set-ssl-w","errorCode":null,"errorMessage":"fail to set ssl: %w","messagePattern":"fail to set ssl: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/controller/domains/domains_v1_set_ssl.go","lineNumber":34,"sourceCode":"\n\tif ex, err1 := domains.Exists(ctx, req.Domain); !ex {\n\t\tif err1 != nil {\n\t\t\tres.SetError(fmt.Errorf(\"fail to check domain: %w\", err1))\n\t\t\treturn\n\t\t}\n\n\t\tres.SetError(fmt.Errorf(\"domain %s does not exist\", req.Domain))\n\t\treturn\n\t}\n\n\tcrt := mail_service.NewCertificate()\n\n\tdefer crt.Close()\n\n\terr = crt.SetSNI(public.FormatMX(req.Domain), req.Certificate, req.Key)\n\n\tif err != nil {\n\t\tres.SetError(fmt.Errorf(\"fail to set ssl: %w\", err))\n\t\treturn\n\t}\n\n\t_ = public.WriteLog(ctx, public.LogParams{\n\t\tType: consts.LOGTYPE.Domain,\n\t\tLog:  \"Set SSL for domain :\" + req.Domain + \" successfully\",\n\t\tData: req,\n\t})\n\n\tres.SetSuccess(public.LangCtx(ctx, \"Success\"))\n\treturn\n}\n","sourceCodeStart":16,"sourceCodeEnd":47,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/controller/domains/domains_v1_set_ssl.go#L16-L47","documentation":"SetSSL wraps any error from crt.SetSNI(public.FormatMX(req.Domain), req.Certificate, req.Key) with this message. SetSNI installs the certificate/key pair as an SNI entry for the mail hostname (mail.example.com) in the mail server config, so failures mean the cert could not be written or applied.","triggerScenarios":"Passing an invalid PEM certificate or mismatched private key in req.Certificate/req.Key; empty cert/key fields; SetSNI failing to update the underlying mail service config; file permission problems writing the cert store.","commonSituations":"Uploading a cert whose key does not match; cert chain in wrong order; pasting a cert with Windows line endings or missing BEGIN/END markers; container lacking write access to the cert directory; Docker volume not mounted.","solutions":["Validate that req.Key is the private key corresponding to req.Certificate (compare modulus/pubkey) and both are valid PEM.","Ensure the full chain (leaf + intermediates) is included and markers/line endings are intact.","Inspect the wrapped inner error (%w) for the exact SetSNI failure and fix filesystem/permission issues it reports.","Restart the mail service if config was partially written, then retry."],"exampleFix":"// before\n{\"certificate\":\"-----BEGIN CERTIFICATE-----\\nMIIB...\", \"key\":\"<ssh key by mistake>\"}\n// after\n{\"certificate\":\"-----BEGIN CERTIFICATE-----\\n...CHAIN...\\n-----END CERTIFICATE-----\", \"key\":\"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\"}","handlingStrategy":"validation","validationCode":"// Pre-flight: match cert and key, and check PEM markers\ncertPub := extractPubKey(certPEM)\nkeyPub := extractPubKey(keyPEM)\nif !bytes.Equal(certPub, keyPub) {\n    return errors.New(\"certificate and private key do not match\")\n}\nif !strings.Contains(certPEM, \"BEGIN CERTIFICATE\") || !strings.Contains(keyPEM, \"BEGIN\") {\n    return errors.New(\"invalid PEM content\")\n}","typeGuard":null,"tryCatchPattern":"if err := setSSL(domain, cert, key); err != nil {\n    var inner error\n    errors.As(err, &inner) // message is 'fail to set ssl: %w'; log the unwrapped cause\n    return fmt.Errorf(\"SSL install failed: %v\", inner)\n}","preventionTips":["Always upload matching cert/key pairs generated together.","Include the full chain and use BEGIN/END PEM markers with LF line endings.","Ensure the cert store directory is writable by the service user in your deployment."],"tags":["ssl","certificate","sni"],"backgroundTag":"ssl-certificate-install-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}