Billionmail/BillionMail · error
unexpected server response
Error message
unexpected server response
What it means
customAuth implements smtp.Auth for PLAIN. Its Next method refuses any server challenge (more == true), because PLAIN sends credentials once in Start and expects no further challenge. If the server keeps the SASL dialogue open, the client rejects it as 'unexpected server response'.
Source
Thrown at core/internal/service/mail_service/sending.go:113
Port string `json:"port" v:"required"` // SMTP server port
Password string `json:"password" v:"required"` // SMTP password
SNI string `json:"sni"` // SNI for TLS
client *smtp.Client // persistent SMTP client connection
mutex sync.Mutex // mutex for thread safety
connected bool // connection status
}
type customAuth struct {
Username, Password string
}
func (a *customAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
return "PLAIN", []byte("\x00" + a.Username + "\x00" + a.Password), nil
}
func (a *customAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
return nil, errors.New("unexpected server response")
}
return nil, nil
}
func NewEmailSender() *EmailSender {
e := &EmailSender{
Host: "localhost",
Port: "25",
connected: false,
}
if public.IsRunningInContainer() {
e.Host = "postfix"
// e.Port = "587"
// e.SNI, _ = public.DockerEnv("BILLIONMAIL_HOSTNAME")
}
return eView on GitHub (pinned to fc36c76c05)
Solutions
- Verify username/password are correct — servers often re-challenge on bad credentials.
- Check the server's advertised AUTH mechanisms (EHLO) and use LOGIN auth if PLAIN isn't the negotiated mechanism.
- Ensure the connection uses TLS if the server only accepts AUTH after STARTTLS.
- If needed, implement a LOGIN customAuth instead of relying on this PLAIN-only implementation.
Example fix
// before
auth := &customAuth{Username: user, Password: pass}
// after
// confirm PLAIN is supported, else fall back
if serverAuths["PLAIN"] {
auth = &customAuth{Username: user, Password: pass}
} else {
auth = smtp.Plain("", user, pass) // or a LOGIN implementation
} Defensive patterns
Strategy: try-catch
Validate before calling
conn, _ := smtp.Dial(host + ":" + port)
defer conn.Close()
conn.Extension("AUTH") // returns mechanisms map; require "PLAIN"
conn.StartTLS(&tls.Config{ServerName: host}) Try / catch
if _, _, err := auth.Start(serverInfo); err == nil {
if _, err := auth.Next(nil, true); err != nil && err.Error() == "unexpected server response" {
// server re-challenged: wrong mechanism or bad credentials
return fmt.Errorf("PLAIN auth rejected by %s; check credentials and AUTH mechanism", host)
}
} Prevention
- EHLO first and confirm PLAIN is among advertised AUTH mechanisms.
- Always authenticate over TLS; some servers misbehave on plaintext AUTH.
- Test credentials with swaks/openssl s_client before wiring them in.
When it happens
Trigger: SMTP server answers the PLAIN auth with an additional challenge (334 prompt) instead of accepting, e.g. server expects a different mechanism (LOGIN/XOAUTH2) or rejects credentials while continuing the exchange.
Common situations: Authenticating to a server configured for LOGIN rather than PLAIN; wrong credentials causing the server to re-prompt; relay/MTA with non-standard SASL behavior.
Related errors
- failed to connect to SMTP server: %v
- failed to send alert email to %d recipient(s): %v
- Email Sender not configured
- Login failed too many times, please try again after %d secon
- Validation code ID and code cannot be empty
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/37215c4bfa5c983e.
Report an issue: GitHub.