jackc/pgx · error
invalid SCRAM server-first-message received from server: did
Error message
invalid SCRAM server-first-message received from server: did not include r=
What it means
Returned by recvServerFirstMessage when the SCRAM server-first-message does not begin with 'r=' (the combined nonce attribute). Per RFC 5802 the server-first-message MUST start with r=. Any other byte indicates a malformed message from the server or an active tampering attempt.
Source
Thrown at pgconn/auth_scram.go:248
sc.clientFirstMessageBare = fmt.Appendf(nil, "n=,r=%s", sc.clientNonce)
switch {
case sc.authMechanism == scramSHA256PlusName:
sc.clientGS2Header = []byte("p=tls-server-end-point,,")
case sc.hasTLS:
sc.clientGS2Header = []byte("y,,")
default:
sc.clientGS2Header = []byte("n,,")
}
return append(sc.clientGS2Header, sc.clientFirstMessageBare...)
}
func (sc *scramClient) recvServerFirstMessage(serverFirstMessage []byte) error {
sc.serverFirstMessage = serverFirstMessage
buf := serverFirstMessage
if !bytes.HasPrefix(buf, []byte("r=")) {
return errors.New("invalid SCRAM server-first-message received from server: did not include r=")
}
buf = buf[2:]
idx := bytes.IndexByte(buf, ',')
if idx == -1 {
return errors.New("invalid SCRAM server-first-message received from server: did not include s=")
}
sc.clientAndServerNonce = buf[:idx]
buf = buf[idx+1:]
if !bytes.HasPrefix(buf, []byte("s=")) {
return errors.New("invalid SCRAM server-first-message received from server: did not include s=")
}
buf = buf[2:]
idx = bytes.IndexByte(buf, ',')
if idx == -1 {
return errors.New("invalid SCRAM server-first-message received from server: did not include i=")View on GitHub (pinned to ec1a0befd2)
Solutions
- Confirm you are connecting to a genuine PostgreSQL (or fully conformant fork) and not a corrupted/debug-proxied endpoint.
- Remove any middleware that rewrites SASL payloads.
- Capture the AuthenticationSASLContinue bytes to confirm the server is the source of the malformed message, then report upstream.
Defensive patterns
Strategy: try-catch
Try / catch
// SCRAM protocol errors are not retryable as-is; surface them clearly.
if err := pgx.Connect(ctx, dsn); err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
// server-origin SQLSTATE error
} else if strings.Contains(err.Error(), "invalid SCRAM server-first-message") {
return fmt.Errorf("malformed SASL exchange (possible MITM/proxy): %w", err)
}
} Prevention
- Use sslmode=verify-full so SASL exchanges cannot be tampered with.
- Avoid debug/proxy tools that rewrite SASL payloads on the wire.
- Regression-test auth against your actual server build, not just vanilla PG.
When it happens
Trigger: Server sends an AuthenticationSASLContinue whose payload does not start with 'r='. Caused by a buggy/non-conformant server, a man-in-the-middle corrupting the SASL exchange, or a protocol-level proxy mangling the message.
Common situations: Custom PostgreSQL-compatible servers; debug proxies rewriting traffic; rare server bugs; network corruption. Almost never seen against vanilla PostgreSQL.
Related errors
- invalid SCRAM server-first-message received from server: did
- invalid SCRAM server-first-message received from server: did
- invalid SCRAM nonce: did not start with client nonce
- invalid SCRAM nonce: did not include server nonce
- invalid SCRAM server-final-message received from server
AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04).
Data as JSON: /data/errors/b70ed92fdaa85586.json.
Report an issue: GitHub.