brianc/node-postgres · error · Error

SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ${sv.itera

Error message

SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration count ${sv.iteration} exceeds scramMaxIterations of ${scramMaxIterations}

What it means

Thrown during SCRAM session continuation (sasl.js:87-94) when the server's requested PBKDF2 iteration count exceeds the client's scramMaxIterations limit (default 100000, configurable via Client option scramMaxIterations; 0 disables the check). The iteration count determines how many PBKDF2 rounds are used to derive the salted key — an absurdly high value could be a denial-of-service vector (each authentication would take extremely long). The client refuses to honor iterations above the cap to bound CPU cost during login. This guard was added as a security hardening measure.

Source

Thrown at packages/pg/lib/crypto/sasl.js:88

    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:
  let channelBinding = stream ? 'eSws' : 'biws' // 'y,,' or 'n,,', base64-encoded

  // override if channel binding is in use:
  if (session.mechanism === 'SCRAM-SHA-256-PLUS') {
    const peerCert = stream.getPeerCertificate().raw
    let hashName = signatureAlgorithmHashFromCertificate(peerCert)
    if (hashName === 'MD5' || hashName === 'SHA-1') hashName = 'SHA-256'

View on GitHub (pinned to c5e8c9a57b)

Solutions

  1. Raise the client cap: new Client({ scramMaxIterations: 200000 }) to match the server's scram_iterations.
  2. Lower the server's scram_iterations to 100000 or below: ALTER SYSTEM SET scram_iterations = 100000.
  3. Set scramMaxIterations: 0 to disable the check entirely (not recommended — removes the DoS protection).

Example fix

// before
const client = new Client({ /* scramMaxIterations defaults to 100000 */ });
// server has scram_iterations=200000

// after
const client = new Client({ scramMaxIterations: 200000 });
Defensive patterns

Strategy: validation

Validate before calling

// Before constructing the client, check or align scramMaxIterations
const SERVER_SCRAM_ITERATIONS = 200000; // from your server config
const client = new Client({
  scramMaxIterations: SERVER_SCRAM_ITERATIONS,
});

Try / catch

try {
  await client.connect();
} catch (err) {
  if (/iteration count.*exceeds scramMaxIterations/i.test(err.message)) {
    // Extract the server's count from the message and raise the client cap
    const match = err.message.match(/iteration count (\d+)/);
    const serverIters = match ? parseInt(match[1], 10) : 200000;
    client2 = new Client({ ...config, scramMaxIterations: serverIters });
    await client2.connect();
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: The server sends an i= value greater than the client's cap. With the default cap of 100000, a server configured with scram_iterations=200000 or higher would trigger it. Setting new Client({ scramMaxIterations: 10000 }) lowers the bar further.

Common situations: A PostgreSQL server (14+) with scram_iterations set very high for extra brute-force resistance. A misconfigured server sending a malformed iteration count. A development/test server with an artificially high value.

Related errors


AI-assisted analysis of brianc/node-postgres@c5e8c9a57b (2026-08-03). Data as JSON: /data/errors/3c20340712aaabfd.json. Report an issue: GitHub.