{"record":{"id":"67ec984eb9130f47","repo":"Billionmail/BillionMail","slug":"smtp-mail-w","errorCode":null,"errorMessage":"SMTP mail: %w","messagePattern":"SMTP mail: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/sending.go","lineNumber":414,"sourceCode":"\t\t\"\\r\\n\"\n\n\tmsg := []byte(headerString)\n\n\tvar err error\n\n\tdefer func() {\n\t\t// Reset the connection state if sending fails\n\t\tif err != nil {\n\t\t\tif err = e.client.Reset(); err != nil {\n\t\t\t\tg.Log().Warning(context.Background(), \"SMTP reset: %w\", err)\n\t\t\t}\n\t\t}\n\t}()\n\n\t// Set the sender\n\t// MAIL FROM\n\tif err = e.client.Mail(e.Email); err != nil {\n\t\treturn fmt.Errorf(\"SMTP mail: %w\", err)\n\t}\n\n\t// Set the recipients\n\t// RCPT TO\n\tfor _, to := range recipients {\n\t\tif err = e.client.Rcpt(to); err != nil {\n\t\t\treturn fmt.Errorf(\"SMTP rcpt: %w\", err)\n\t\t}\n\t}\n\n\t// Get a writer for the message body\n\tvar w io.WriteCloser\n\tw, err = e.client.Data()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"SMTP data: %w\", err)\n\t}\n\n\t// Write the message","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/sending.go#L396-L432","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nif err = e.client.Mail(e.Email); err != nil {\n    return fmt.Errorf(\"SMTP mail: %w\", err)\n}\n// after\nif err = e.client.Mail(e.Email); err != nil {\n    if errors.Is(err, io.EOF) || errors.Is(err, syscall.EPIPE) {\n        return fmt.Errorf(\"SMTP mail: stale connection: %w\", err) // triggers Send's reconnect path\n    }\n    return fmt.Errorf(\"SMTP mail (from %s): %w\", e.Email, err)\n}","handlingStrategy":"try-catch","validationCode":"from, err := mail.ParseAddress(senderEmail)\nif err != nil {\n    return fmt.Errorf(\"invalid envelope sender %q: %w\", senderEmail, err)\n}\n// also verify the domain is one the relay account is authorized to send from","typeGuard":"func isMailFromRejected(err error) bool {\n    var protoErr *textproto.Error\n    if !errors.As(err, &protoErr) {\n        return false\n    }\n    switch {\n    case protoErr.Code == 501:\n        return true // syntax\n    case protoErr.Code >= 550 && protoErr.Code <= 553:\n        return true // policy / sender rejected\n    }\n    return false\n}","tryCatchPattern":"if err := sender.Send(msg, rcpts); err != nil {\n    if strings.Contains(err.Error(), \"SMTP mail\") {\n        if isMailFromRejected(err) {\n            return fmt.Errorf(\"envelope sender %q rejected by relay (verify domain/SPF): %w\", fromAddr, err)\n        }\n        if errors.Is(err, io.EOF) {\n            time.Sleep(2 * time.Second)\n            return sender.Send(msg, rcpts) // stale session: retry reconnects\n        }\n    }\n    return err\n}","preventionTips":["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."],"tags":["smtp","mail-from","envelope","protocol"],"backgroundTag":"smtp-mail-from-rejected","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"}