Billionmail/BillionMail · error
SMTP mail: %w
Error message
SMTP mail: %w
What it means
doSend's first SMTP transaction command, client.Mail(e.Email) (MAIL FROM), failed — the server rejected the envelope sender or the connection was in a bad state. The wrapped error contains the server's 4xx/5xx reply (e.g. 501 bad address syntax, 550 relay denied, 553 sender not permitted). Note e.Email is the configured envelope sender, distinct from any From header.
Source
Thrown at core/internal/service/mail_service/sending.go:414
"\r\n"
msg := []byte(headerString)
var err error
defer func() {
// Reset the connection state if sending fails
if err != nil {
if err = e.client.Reset(); err != nil {
g.Log().Warning(context.Background(), "SMTP reset: %w", err)
}
}
}()
// Set the sender
// MAIL FROM
if err = e.client.Mail(e.Email); err != nil {
return fmt.Errorf("SMTP mail: %w", err)
}
// Set the recipients
// RCPT TO
for _, to := range recipients {
if err = e.client.Rcpt(to); err != nil {
return fmt.Errorf("SMTP rcpt: %w", err)
}
}
// Get a writer for the message body
var w io.WriteCloser
w, err = e.client.Data()
if err != nil {
return fmt.Errorf("SMTP data: %w", err)
}
// Write the messageView on GitHub (pinned to fc36c76c05)
Solutions
- Read the wrapped server reply to distinguish syntax errors (501), policy rejection (550/553), or connection errors (EOF — treat as the reconnect case).
- Make e.Email a valid address on a domain the account is authorized to send from; match the authenticated username when the relay requires it.
- Verify the sending domain is configured/verified on the mail server (SPF/DKIM/relay allowlist).
- If the error is an EOF/protocol error, reconnect and retry once — the session was stale rather than the address rejected.
Example fix
// before
if err = e.client.Mail(e.Email); err != nil {
return fmt.Errorf("SMTP mail: %w", err)
}
// after
if err = e.client.Mail(e.Email); err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, syscall.EPIPE) {
return fmt.Errorf("SMTP mail: stale connection: %w", err) // triggers Send's reconnect path
}
return fmt.Errorf("SMTP mail (from %s): %w", e.Email, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
from, err := mail.ParseAddress(senderEmail)
if err != nil {
return fmt.Errorf("invalid envelope sender %q: %w", senderEmail, err)
}
// also verify the domain is one the relay account is authorized to send from Type guard
func isMailFromRejected(err error) bool {
var protoErr *textproto.Error
if !errors.As(err, &protoErr) {
return false
}
switch {
case protoErr.Code == 501:
return true // syntax
case protoErr.Code >= 550 && protoErr.Code <= 553:
return true // policy / sender rejected
}
return false
} Try / catch
if err := sender.Send(msg, rcpts); err != nil {
if strings.Contains(err.Error(), "SMTP mail") {
if isMailFromRejected(err) {
return fmt.Errorf("envelope sender %q rejected by relay (verify domain/SPF): %w", fromAddr, err)
}
if errors.Is(err, io.EOF) {
time.Sleep(2 * time.Second)
return sender.Send(msg, rcpts) // stale session: retry reconnects
}
}
return err
} Prevention
- Parse the envelope sender with net/mail.ParseAddress before configuring the sender.
- Ensure MAIL FROM matches the authenticated user when the relay enforces it.
- Verify SPF/DKIM/domain registration for the sending domain at the provider.
- Keep NOOP keepalives on idle connections to avoid stale-session protocol errors at MAIL FROM.
When it happens
Trigger: doSend called with an e.Email the server refuses: malformed address, domain not allowed for that relay account, sender must match the authenticated user, or the SMTP session was already broken so the command got an EOF/protocol error.
Common situations: Relay enforces MAIL FROM equals the authenticated user (mismatch in configured e.Email); sending domain not verified at the provider (550 sender rejected); e.Email empty or containing invalid characters; server dropped the connection after long idle before MAIL FROM; rate/relay limits (452/454).
Related errors
- new SMTP client: %w
- SMTP server is required
- SMTP port must be between 1 and 65535
- SMTP password is required
- at least one recipient is required
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/67ec984eb9130f47.
Report an issue: GitHub.