{"record":{"id":"37215c4bfa5c983e","repo":"Billionmail/BillionMail","slug":"unexpected-server-response","errorCode":null,"errorMessage":"unexpected server response","messagePattern":"unexpected server response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/mail_service/sending.go","lineNumber":113,"sourceCode":"\tPort      string       `json:\"port\" v:\"required\"`     // SMTP server port\n\tPassword  string       `json:\"password\" v:\"required\"` // SMTP password\n\tSNI       string       `json:\"sni\"`                   // SNI for TLS\n\tclient    *smtp.Client // persistent SMTP client connection\n\tmutex     sync.Mutex   // mutex for thread safety\n\tconnected bool         // connection status\n}\n\ntype customAuth struct {\n\tUsername, Password string\n}\n\nfunc (a *customAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {\n\treturn \"PLAIN\", []byte(\"\\x00\" + a.Username + \"\\x00\" + a.Password), nil\n}\n\nfunc (a *customAuth) Next(fromServer []byte, more bool) ([]byte, error) {\n\tif more {\n\t\treturn nil, errors.New(\"unexpected server response\")\n\t}\n\treturn nil, nil\n}\n\nfunc NewEmailSender() *EmailSender {\n\te := &EmailSender{\n\t\tHost:      \"localhost\",\n\t\tPort:      \"25\",\n\t\tconnected: false,\n\t}\n\n\tif public.IsRunningInContainer() {\n\t\te.Host = \"postfix\"\n\t\t// e.Port = \"587\"\n\t\t// e.SNI, _ = public.DockerEnv(\"BILLIONMAIL_HOSTNAME\")\n\t}\n\n\treturn e","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/mail_service/sending.go#L95-L131","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nauth := &customAuth{Username: user, Password: pass}\n// after\n// confirm PLAIN is supported, else fall back\nif serverAuths[\"PLAIN\"] {\n    auth = &customAuth{Username: user, Password: pass}\n} else {\n    auth = smtp.Plain(\"\", user, pass) // or a LOGIN implementation\n}","handlingStrategy":"try-catch","validationCode":"conn, _ := smtp.Dial(host + \":\" + port)\ndefer conn.Close()\nconn.Extension(\"AUTH\") // returns mechanisms map; require \"PLAIN\"\nconn.StartTLS(&tls.Config{ServerName: host})","typeGuard":null,"tryCatchPattern":"if _, _, err := auth.Start(serverInfo); err == nil {\n    if _, err := auth.Next(nil, true); err != nil && err.Error() == \"unexpected server response\" {\n        // server re-challenged: wrong mechanism or bad credentials\n        return fmt.Errorf(\"PLAIN auth rejected by %s; check credentials and AUTH mechanism\", host)\n    }\n}","preventionTips":["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."],"tags":["smtp","authentication","sasl","email"],"backgroundTag":"smtp-auth-unexpected-server-response","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}