brianc/node-postgres · critical · Error

SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missin

Error message

SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing

What it means

Thrown by parseServerFinalMessage when the final SASL message contains neither `e=` nor `v=` (sasl.js:231, serverSignature is falsy and error was absent). RFC 5802 requires at least one of them, so a frame with neither is non-conformant.

Source

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

  return {
    nonce,
    salt,
    iteration,
  }
}

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

  if (error) {
    throw new Error(`SASL: SCRAM-SERVER-FINAL-MESSAGE: server returned error: "${error}"`)
  }

  if (!serverSignature) {
    throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing')
  } else if (!isBase64(serverSignature)) {
    throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature must be base64')
  }
  return {
    serverSignature,
  }
}

function xorBuffers(a, b) {
  if (!Buffer.isBuffer(a)) {
    throw new TypeError('first argument must be a Buffer')
  }
  if (!Buffer.isBuffer(b)) {
    throw new TypeError('second argument must be a Buffer')
  }
  if (a.length !== b.length) {
    throw new Error('Buffer lengths must match')
  }

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 end-to-end.
  3. Inspect wire frames to verify the final message is delivered intact.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await client.connect()
} catch (err) {
  if (/server signature is missing/.test(err.message)) {
    logger.error('SCRAM final frame had neither v= nor e=', { err })
    return connectBypassingPooler()
  }
  throw err
}

Prevention

When it happens

Trigger: finalizeSession parses a final server message whose only attribute pairs are unrelated to `v` or `e`; serverSignature = attrPairs.get('v') at line 225 returns undefined and error at line 224 is also undefined.

Common situations: Frame truncation by a pooler/proxy; non-PostgreSQL server; intermediary rewriting the final SASL message.

Related errors


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