Billionmail/BillionMail · error
TLS dial: %w
Error message
TLS dial: %w
What it means
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.
Source
Thrown at core/internal/service/mail_service/sending.go:214
if err != nil {
e.connected = false
e.client = nil
return err
}
e.connected = true
return nil
}
// connectWithSSL establishes a secure SMTP connection
func (e *EmailSender) connectWithSSL() error {
conn, err := tls.Dial("tcp", net.JoinHostPort(e.Host, e.Port), &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: true,
ServerName: e.SNI,
})
if err != nil {
return fmt.Errorf("TLS dial: %w", err)
}
client, err := smtp.NewClient(conn, e.Host)
if err != nil {
conn.Close()
return fmt.Errorf("new SMTP client: %w", err)
}
auth := smtp.PlainAuth("", e.UserName, e.Password, e.Host)
if err = client.Auth(auth); err != nil {
client.Close()
return fmt.Errorf("SMTP auth: %w", err)
}
e.client = client
return nil
}View on GitHub (pinned to fc36c76c05)
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.
Example fix
// before
conn, err := tls.Dial("tcp", net.JoinHostPort(e.Host, e.Port), &tls.Config{ServerName: e.SNI})
// after
if e.SNI == "" {
e.SNI = e.Host // ensure ServerName is set so the handshake presents correct SNI
}
addr := net.JoinHostPort(e.Host, e.Port)
if !portReachable(e.Host, e.Port) { // pre-check with net.DialTimeout
return fmt.Errorf("TLS dial: server %s unreachable", addr)
}
conn, err := tls.Dial("tcp", addr, &tls.Config{ServerName: e.SNI}) Defensive patterns
Strategy: validation
Validate before calling
func smtpsReachable(host, port string) error {
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), 5*time.Second)
if err != nil {
return fmt.Errorf("cannot reach %s:%s: %w", host, port, err)
}
conn.Close()
return nil
}
// call before creating/using the sender
if err := smtpsReachable(cfg.SMTPHost, cfg.SMTPPort); err != nil { log.Fatal(err) } Type guard
func isDialErr(err error) bool {
var opErr *net.OpError
var certErr *tls.CertificateVerificationError
return errors.As(err, &opErr) || errors.As(err, &certErr)
} Try / catch
if err := sender.Send(msg, rcpts); err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
// transient: retry with backoff
} else {
return fmt.Errorf("TLS dial failed permanently: %w", err)
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- SMTP dial: %w
- %s
- failed to connect to SMTP server: %v
- failed to connect to SMTP server: %v
- new SMTP client: %w
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/11dcf8f6ca2558de.
Report an issue: GitHub.