Billionmail/BillionMail · error
SMTP data: %w
Error message
SMTP data: %w
What it means
This error wraps a failure of net/smtp Client.Data(), which issues the SMTP DATA command. The server refused to enter data mode (usually a 4xx/5xx reply) after envelope sender and recipients were accepted. No message body was transmitted.
Source
Thrown at core/internal/service/mail_service/sending.go:429
// 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 message
if _, err = w.Write(msg); err != nil {
return fmt.Errorf("SMTP write: %w", err)
}
// Close the writer
if err = w.Close(); err != nil {
return fmt.Errorf("SMTP close writer: %w", err)
}
return nil
}
// isSecure check if TLS connection is needed
func (e *EmailSender) isSecure() bool {
// usually,View on GitHub (pinned to fc36c76c05)
Solutions
- Check mail server logs for the reply code returned at DATA to identify policy rejection vs connection issue
- Retry with a fresh SMTP connection — the session may be stale; the deferred Reset() already attempts to clear state
- If a milter (rspamd) rejects at DATA, inspect milter settings and message headers triggering the rejection
- Ensure keepalives/short transaction times so the connection doesn't idle out between commands
Defensive patterns
Strategy: retry
Validate before calling
if sender == nil || !connectionHealthy() {
return errors.New("SMTP session not ready for DATA")
} Try / catch
err := sender.Send(msg, recipients)
if err != nil {
if isTransient(err) { // 4xx or io.EOF
backoffRetry(3)
} else {
logAndFail(err)
}
} Prevention
- Re-establish the SMTP connection instead of reusing long-idle sessions
- Check mail server milter/policy settings if DATA rejections are frequent
- Monitor mail logs for greylisting/throttling and add retry with backoff
- Keep transaction duration short to avoid server-side timeouts
When it happens
Trigger: e.client.Data() returned an error — commonly a '554 transaction failed' reply, a connection that dropped after RCPT TO, or the server rejecting the transaction due to policy (spam filter, greylisting) between RCPT and DATA.
Common situations: Postfix smtpd bans or throttles the client after the envelope; connection idle-timed out mid-transaction; rspamd/milter rejects the transaction at DATA stage; network interruption during the SMTP session.
Related errors
- failed to send test email to %d recipient(s): %v
- SMTP rcpt: %w
- SMTP close writer: %w
- SMTP server is required
- SMTP port must be between 1 and 65535
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/7eb6d173e4638223.
Report an issue: GitHub.