semaphoreui/semaphore · error
unexpected server challenge
Error message
unexpected server challenge
What it means
With authMethod "PLAIN", the client sends username and password in a single initial response, so no further server challenge is expected. If the server sends another challenge during Next, the state machine cannot continue and this error is returned. It usually indicates the server does not actually support PLAIN as advertised.
Solutions
- Force the LOGIN mechanism (remove PLAIN from the server's advertised mechanisms or configure the client to prefer LOGIN).
- Fix or replace the SMTP server/proxy so it honors RFC 4616 PLAIN semantics (single message, no follow-up challenge).
- Capture the SMTP session and verify which AUTH mechanisms the server actually advertises after STARTTLS.
Example fix
// before
// server advertises ["PLAIN","LOGIN"] but only implements LOGIN
// after (client side preference)
if !slices.Contains(server.Auth, "PLAIN") || serverBrokenPLAIN {
a.authMethod = "LOGIN" // use LOGIN flow for challenge/response
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify the server truly supports PLAIN before selecting it
if !slices.Contains(serverInfo.Auth, "PLAIN") {
return errors.New("server does not advertise PLAIN; use LOGIN instead")
} Try / catch
_, _, err := auth.Next(fromServer, true)
if err != nil && errors.Is(err, errUnexpectedChallenge) || err.Error() == "unexpected server challenge" {
// fall back to LOGIN mechanism and restart AUTH
return restartAuthWithLogin(client, username, password)
} Prevention
- Verify the SMTP server's advertised AUTH mechanisms after STARTTLS, not just its config.
- Prefer PLAIN only over verified TLS with servers known to implement RFC 4616 one-shot semantics.
- Remove PLAIN from servers that only implement LOGIN to avoid mechanism mismatch.
- Capture SMTP session traces when debugging third-party relay auth.
When it happens
Trigger: Server advertises PLAIN in its AUTH mechanisms (so plainAuth is selected) but then issues an additional challenge instead of accepting the one-shot response - misbehaving or misconfigured SMTP server.
Common situations: Relay advertising PLAIN but implemented as LOGIN-only; middleware/proxy mangling the AUTH exchange; server state inconsistent after STARTTLS.
Related errors
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/b55996102c31ffca.
Report an issue: GitHub.
Appendix: source
Thrown at util/mailer/auth.go:55
}
if !slices.Contains(server.Auth, "PLAIN") {
a.authMethod = "LOGIN"
return a.authMethod, nil, nil
} else {
a.authMethod = "PLAIN"
resp := []byte("\x00" + a.username + "\x00" + a.password)
return a.authMethod, resp, nil
}
}
func (a *plainOrLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if !more {
return nil, nil
}
if a.authMethod == "PLAIN" {
// We've already sent everything.
return nil, errors.New("unexpected server challenge")
}
switch {
case bytes.Equal(fromServer, []byte("Username:")):
return []byte(a.username), nil
case bytes.Equal(fromServer, []byte("Password:")):
return []byte(a.password), nil
default:
return nil, fmt.Errorf("unexpected server challenge: %s", fromServer)
}
}
View on GitHub (pinned to 1774ccb71a)