brianc/node-postgres · critical · Error

SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing

Error message

SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing

What it means

Thrown by parseServerFirstMessage when the server's first SCRAM message lacks the required `i=` (iteration count) attribute entirely (sasl.js:208, iterationText is falsy). RFC 5802 mandates this attribute, so its absence indicates a non-conformant or truncated server response.

Source

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

function parseServerFirstMessage(data) {
  const attrPairs = parseAttributePairs(data)

  const nonce = attrPairs.get('r')
  if (!nonce) {
    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing')
  } else if (!isPrintableChars(nonce)) {
    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters')
  }
  const salt = attrPairs.get('s')
  if (!salt) {
    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing')
  } else if (!isBase64(salt)) {
    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64')
  }
  const iterationText = attrPairs.get('i')
  if (!iterationText) {
    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing')
  } else if (!/^[1-9][0-9]*$/.test(iterationText)) {
    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count')
  }
  const iteration = parseInt(iterationText, 10)

  return {
    nonce,
    salt,
    iteration,
  }
}

function parseServerFinalMessage(serverData) {
  const attrPairs = parseAttributePairs(serverData)
  const error = attrPairs.get('e')
  const serverSignature = attrPairs.get('v')

  if (error) {

View on GitHub (pinned to c5e8c9a57b)

Solutions

  1. Connect directly to PostgreSQL bypassing any pooler/proxy.
  2. Confirm the pooler/proxy version supports SCRAM-SHA-256 passthrough.
  3. Verify TLS integrity end-to-end (no frame-rewriting middlebox).
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await client.connect()
} catch (err) {
  if (/iteration missing/.test(err.message)) {
    logger.error('SCRAM final frame missing i= attribute -- likely intermediary truncation', { err })
    return connectDirectlyToPrimary()
  }
  throw err
}

Prevention

When it happens

Trigger: continueSession receives serverData with no `i=...` pair in the comma-separated attribute list produced by parseAttributePairs (sasl.js:175). attrPairs.get('i') returns undefined.

Common situations: Same proxy/pooler frame-mangling family as the salt error; a NAT/MITM device truncating the auth exchange; a server implementing an outdated or non-standard SASL profile.

Related errors


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