brianc/node-postgres · error · Error
SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short
Error message
SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce is too short
What it means
Thrown during SCRAM session continuation (sasl.js:80-81) when the server's nonce has the exact same length as the client nonce — meaning the server appended nothing. Per RFC 5802, the server MUST append its own per-session nonce to the client's nonce to ensure freshness; a server that returns only the client nonce (no additional entropy) is non-compliant and could indicate a broken or malicious endpoint. The check is sv.nonce.length === session.clientNonce.length.
Source
Thrown at packages/pg/lib/crypto/sasl.js:81
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.iteration
// without channel binding:View on GitHub (pinned to c5e8c9a57b)
Solutions
- Verify you are connecting to a genuine PostgreSQL backend (not a non-compliant proxy/mock).
- If behind PgBouncer or a connection pooler, ensure it passes SCRAM messages through unmodified.
- Enable SSL to protect the authentication exchange from tampering.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await client.connect();
} catch (err) {
if (/server nonce is too short/i.test(err.message)) {
console.error('Server did not append its own nonce — non-compliant server or proxy.');
}
throw err;
} Prevention
- Connect to a genuine, compliant PostgreSQL backend.
- Ensure PgBouncer or poolers pass SCRAM messages through unmodified.
- Use SSL to prevent message tampering during auth.
When it happens
Trigger: The server's r= value equals the clientNonce exactly (same length, starts with it per the previous check but adds no server-side entropy). This would mean the server sent back r=<clientNonce> with nothing appended.
Common situations: A poorly implemented PostgreSQL-compatible proxy or mock that does not generate a server nonce. A replay device that echoes the client's first message. Extremely rare with genuine PostgreSQL servers.
Related errors
- SASL: SCRAM-SERVER-FIRST-MESSAGE: server nonce does not star
- 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/370116b464558d22.json.
Report an issue: GitHub.