brianc/node-postgres · error · Error

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

Error message

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

What it means

Thrown by parseServerFinalMessage() when the server's final SASL message lacks the v= attribute (verifier/signature) AND has no e= attribute (error). After confirming there is no error, the parser expects a server signature to verify. Its absence means the server sent an incomplete final message.

Solutions

  1. Verify the target is a standard PostgreSQL server with full SCRAM-SHA-256 support.
  2. Remove intermediaries that might truncate the SASL final message.
  3. Test the connection with psql using the same connection string.
  4. Enable SSL/TLS to protect the authentication stream.
  5. Update node-postgres to the latest version.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await client.connect()
} catch (err) {
  if (err.message.includes('server signature is missing')) {
    throw new Error('SCRAM final message missing signature — verify server is PostgreSQL 10+ and no proxy truncates traffic')
  }
  throw err
}

Prevention

When it happens

Trigger: At sasl.js:231-232, attrPairs.get('e') is falsy (no error) and attrPairs.get('v') is also falsy (no signature). The server's final message contains neither an error nor a verifier — it is missing the critical v= attribute.

Common situations: Malformed or truncated server final message; a non-conformant server that doesn't fully implement the SCRAM final message format; a proxy truncating the authentication exchange; network data loss cutting the final message before the signature attribute.

Related errors


AI-assisted analysis of brianc/node-postgres@ff9d775abd (2026-08-11). Data as JSON: /api/errors/02487e9378f404a8. Report an issue: GitHub.

Appendix: 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 ff9d775abd)