{"record":{"id":"11dcf8f6ca2558de","repo":"Billionmail/BillionMail","slug":"tls-dial-w","errorCode":null,"errorMessage":"TLS dial: %w","messagePattern":"TLS dial: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/sending.go","lineNumber":214,"sourceCode":"\tif err != nil {\n\t\te.connected = false\n\t\te.client = nil\n\t\treturn err\n\t}\n\n\te.connected = true\n\treturn nil\n}\n\n// connectWithSSL establishes a secure SMTP connection\nfunc (e *EmailSender) connectWithSSL() error {\n\tconn, err := tls.Dial(\"tcp\", net.JoinHostPort(e.Host, e.Port), &tls.Config{\n\t\tMinVersion:         tls.VersionTLS12,\n\t\tInsecureSkipVerify: true,\n\t\tServerName:         e.SNI,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"TLS dial: %w\", err)\n\t}\n\n\tclient, err := smtp.NewClient(conn, e.Host)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn fmt.Errorf(\"new SMTP client: %w\", err)\n\t}\n\n\tauth := smtp.PlainAuth(\"\", e.UserName, e.Password, e.Host)\n\tif err = client.Auth(auth); err != nil {\n\t\tclient.Close()\n\t\treturn fmt.Errorf(\"SMTP auth: %w\", err)\n\t}\n\n\te.client = client\n\n\treturn nil\n}","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/sending.go#L196-L232","documentation":"This error wraps the failure of tls.Dial when connectWithSSL tries to open an implicit-TLS (port 465) SMTP connection to e.Host:e.Port. It fires before any SMTP conversation starts, so the root cause is a TCP/TLS-level problem: server unreachable, not listening for TLS, or rejecting the handshake. The original net/tls error is preserved via %w for errors.Is/errors.As inspection.","triggerScenarios":"EmailSender.Connect() is called with a secure host/port (isSecure() true, typically port 465) and tls.Dial fails: host unresolvable, port blocked/closed, or TLS handshake rejected by the remote server.","commonSituations":"Wrong port for implicit SSL (e.g. using 587 with connectWithSSL, since 587 expects STARTTLS); firewall/Docker network blocking outbound 465; hostname typo or internal DNS not resolving; remote mail server down; SNI (e.SNI) empty or mismatched causing handshake failure on strict servers.","solutions":["Verify the SMTP host resolves and the port is reachable: nc -zv <host> <port> or openssl s_client -connect host:port.","Make sure port matches the security mode: 465 for implicit TLS (connectWithSSL), 587/25 for STARTTLS/plain (connectPlain).","Check network/firewall rules allow outbound connections to that port from the app host/container.","Use tcpdump or openssl s_client to confirm the TCP handshake completes and inspect the TLS alert the server returns."],"exampleFix":"// before\nconn, err := tls.Dial(\"tcp\", net.JoinHostPort(e.Host, e.Port), &tls.Config{ServerName: e.SNI})\n// after\nif e.SNI == \"\" {\n    e.SNI = e.Host // ensure ServerName is set so the handshake presents correct SNI\n}\naddr := net.JoinHostPort(e.Host, e.Port)\nif !portReachable(e.Host, e.Port) { // pre-check with net.DialTimeout\n    return fmt.Errorf(\"TLS dial: server %s unreachable\", addr)\n}\nconn, err := tls.Dial(\"tcp\", addr, &tls.Config{ServerName: e.SNI})","handlingStrategy":"validation","validationCode":"func smtpsReachable(host, port string) error {\n    conn, err := net.DialTimeout(\"tcp\", net.JoinHostPort(host, port), 5*time.Second)\n    if err != nil {\n        return fmt.Errorf(\"cannot reach %s:%s: %w\", host, port, err)\n    }\n    conn.Close()\n    return nil\n}\n// call before creating/using the sender\nif err := smtpsReachable(cfg.SMTPHost, cfg.SMTPPort); err != nil { log.Fatal(err) }","typeGuard":"func isDialErr(err error) bool {\n    var opErr *net.OpError\n    var certErr *tls.CertificateVerificationError\n    return errors.As(err, &opErr) || errors.As(err, &certErr)\n}","tryCatchPattern":"if err := sender.Send(msg, rcpts); err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) && netErr.Timeout() {\n        // transient: retry with backoff\n    } else {\n        return fmt.Errorf(\"TLS dial failed permanently: %w\", err)\n    }\n}","preventionTips":["Match port to TLS mode: 465 implicit TLS, 587 STARTTLS.","Set SNI/ServerName to the SMTP hostname; never leave it empty.","Pre-flight the endpoint with openssl s_client before deploying config changes.","Confirm outbound firewall rules allow the SMTP port from the app host."],"tags":["network","tls","smtp","dial"],"backgroundTag":"tls-handshake-failed","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"}