brianc/node-postgres · error · Error
SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not star
Error message
SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce
What it means
Thrown during SCRAM session continuation (sasl.js:78-79) when the server's combined nonce (r=...) does not begin with the client's nonce. Per RFC 5802, the server must return the client nonce unchanged with its own nonce appended; a server nonce that does not start with the client nonce indicates either a protocol violation or a potential man-in-the-middle/replay attack where the server response was substituted. The client sent r=<clientNonce> in the first message and verifies the server echoed it back as a prefix.
Source
Thrown at packages/pg/lib/crypto/sasl.js:79
async function continueSession(session, password, serverData, stream) {
if (session.message !== 'SASLInitialResponse') {
throw new Error('SASL: Last message was not SASLInitialResponse')
}
if (typeof password !== 'string') {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string')
}
if (password === '') {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a non-empty string')
}
if (typeof serverData !== 'string') {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: serverData must be a string')
}
const sv = parseServerFirstMessage(serverData)
if (!sv.nonce.startsWith(session.clientNonce)) {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not start with client nonce')
} else if (sv.nonce.length === session.clientNonce.length) {
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short')
}
const scramMaxIterations =
typeof session.scramMaxIterations === 'number' ? session.scramMaxIterations : DEFAULT_MAX_SCRAM_ITERATIONS
// a value of 0 disables the iteration count check
if (scramMaxIterations !== 0 && sv.iteration > scramMaxIterations) {
throw new Error(
'SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ' +
sv.iteration +
' exceeds scramMaxIterations of ' +
scramMaxIterations
)
}
const clientFirstMessageBare = 'n=*,r=' + session.clientNonce
const serverFirstMessage = 'r=' + sv.nonce + ',s=' + sv.salt + ',i=' + sv.iterationView on GitHub (pinned to c5e8c9a57b)
Solutions
- Verify there is no proxy or MITM between the client and PostgreSQL (check PgBouncer, load balancers, SSL termination).
- If using SSL, ensure it is properly established so the SCRAM exchange is integrity-protected.
- If connecting to a custom PostgreSQL-compatible server, verify its SCRAM implementation correctly echoes the client nonce.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await client.connect();
} catch (err) {
if (/server nonce does not start with client nonce/i.test(err.message)) {
console.error('Possible MITM or non-compliant server in SCRAM exchange. Verify SSL and network path.');
}
throw err;
} Prevention
- Use SSL/TLS to protect the SCRAM exchange from tampering or injection.
- Verify there is no non-compliant proxy between client and server.
- Connect directly to PostgreSQL when debugging auth issues to isolate intermediaries.
When it happens
Trigger: The server's first SCRAM message contains an r= value whose first bytes do not match the clientNonce generated at sasl.js:50. This is checked via sv.nonce.startsWith(session.clientNonce).
Common situations: A buggy or non-compliant PostgreSQL server or proxy. An active MITM attempting to relay a different SCRAM session. Rare: data corruption on the wire truncating the nonce. This is extremely uncommon with real PostgreSQL servers.
Related errors
- SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short
- SASL: Only mechanism(s) ${candidates.join(' and ')} are supp
- SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ${sv.itera
- SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature does not
- SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain pr
AI-assisted analysis of brianc/node-postgres@c5e8c9a57b (2026-08-03).
Data as JSON: /data/errors/21a0a05644ec8c02.json.
Report an issue: GitHub.