{"record":{"id":"9b16252bf1959b98","repo":"Billionmail/BillionMail","slug":"failed-to-apply-for-ssl-certificate","errorCode":null,"errorMessage":"Failed to apply for SSL certificate: {}","messagePattern":"Failed to apply for SSL certificate: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/acme/acme.go","lineNumber":465,"sourceCode":"\t\treg, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})\n\t\tif err != nil {\n\t\t\treturn \"\", \"\", errors.New(public.LangCtx(ctx, \"Failed to register user: {}\", err.Error()))\n\t\t}\n\t}\n\n\t// Save user information\n\tmyUser.Registration = reg\n\n\t// Submit application\n\trequest := certificate.ObtainRequest{\n\t\tDomains: domains,\n\t\tBundle:  true,\n\t}\n\n\t// Get certificate\n\tcertificates, err := client.Certificate.Obtain(request)\n\tif err != nil {\n\t\treturn \"\", \"\", errors.New(public.LangCtx(ctx, \"Failed to apply for SSL certificate: {}\", err.Error()))\n\t}\n\n\t// Save certificate files if path is provided\n\tif savePath != \"\" {\n\t\t// Create directory if it doesn't exist\n\t\tif !public.FileExists(savePath) {\n\t\t\terr = os.MkdirAll(savePath, 0750)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", \"\", errors.New(public.LangCtx(ctx, \"Failed to create directory: {}\", err.Error()))\n\t\t\t}\n\t\t}\n\n\t\t// Save certificate and private key files\n\t\tcertificateFile := filepath.Join(savePath, \"certificate.pem\")\n\t\tprivateKeyFile := filepath.Join(savePath, \"private_key.pem\")\n\n\t\t_, err = public.WriteFile(certificateFile, string(certificates.Certificate))\n\t\tif err != nil {","sourceCodeStart":447,"sourceCodeEnd":483,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/acme/acme.go#L447-L483","documentation":"The final ACME step calls client.Certificate.Obtain(request) to run the chosen challenge and fetch the certificate. This error wraps every failure lego reports during that flow — challenge validation failures (HTTP-01 on 127.0.0.1:60880 unreachable by the CA, or DNS-01 TXT record wrong/propagating), authorization failures, and CA-side rate limits. The wrapped err text names the exact failing domain and challenge type.","triggerScenarios":"ApplySSLWithExistingServer (via Apply, StartRenew, ApplyLetsEncryptCertWithHttp, ApplyConsoleCert) where the CA cannot validate the challenge: for http-01, the CA cannot reach http://<domain>/.well-known/acme-challenge (port 80 closed or not forwarded to 127.0.0.1:60880); for dns-01, the TXT record was not created/propagated or credentials lack zone write access.","commonSituations":"Firewall/NAT blocking port 80 in front of the challenge server; domain's DNS pointing elsewhere than the server running this code; DNS propagation delay; Let's Encrypt rate limits after repeated failures; expired wildcard attempts using dns provider with wrong zone.","solutions":["Read the wrapped err to identify the failing domain/challenge; for http-01 verify the CA can reach the domain on port 80 and that traffic reaches the challenge server on 127.0.0.1:60880 (open/forward port 80, disable blocking proxy rules)","For dns-01, confirm the DNS provider token works and the TXT record appears (_acme-challenge.<domain>) via dig before retrying","Wait out rate limits or use the Let's Encrypt staging CA while debugging, then retry with production"],"exampleFix":"// before\n// port 80 closed; CA cannot reach challenge\n// after\n// open port 80 in firewall/NAT so http://<domain>/.well-known/acme-challenge/ reaches 127.0.0.1:60880","handlingStrategy":"retry","validationCode":"// pre-flight: port 80 reachable and DNS points here (http-01)\nif vtype == \"http\" {\n    ip, _ := net.LookupIP(domain)\n    pub, _ := externalIP()\n    if len(ip) == 0 || ip[0].String() != pub { return errors.New(\"domain does not resolve to this server\") }\n}\n// dns-01: check TXT after provider setup\n// dig TXT _acme-challenge.<domain> +short","typeGuard":"func challengeReachable(vtype, domain string) bool {\n    if vtype == \"http\" {\n        conn, err := net.DialTimeout(\"tcp\", domain+\":80\", 5*time.Second)\n        if err != nil { return false }\n        conn.Close()\n    }\n    return true\n}","tryCatchPattern":"cert, _, err := ApplySSLWithExistingServer(ctx, ...)\nif err != nil && strings.Contains(err.Error(), \"Failed to apply for SSL certificate\") {\n    // challenge failures are often transient (propagation, rate limits)\n    time.Sleep(1 * time.Minute)\n    return retryApplyWithBackoff(ctx, 3)\n}","preventionTips":["Ensure port 80 is open/forwarded to the challenge server for http-01","Verify TXT record propagation before retrying dns-01","Use the staging CA while debugging to avoid rate limits","Confirm the domain's DNS A record points at the machine running this code"],"tags":["acme","ssl","challenge","letsencrypt"],"backgroundTag":"acme-challenge-validation-failed","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"}