{"id":"02487e9378f404a8","repo":"brianc/node-postgres","slug":"sasl-scram-server-final-message-server-signature-02487e","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing","messagePattern":"SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":232,"sourceCode":"\n  return {\n    nonce,\n    salt,\n    iteration,\n  }\n}\n\nfunction parseServerFinalMessage(serverData) {\n  const attrPairs = parseAttributePairs(serverData)\n  const error = attrPairs.get('e')\n  const serverSignature = attrPairs.get('v')\n\n  if (error) {\n    throw new Error(`SASL: SCRAM-SERVER-FINAL-MESSAGE: server returned error: \"${error}\"`)\n  }\n\n  if (!serverSignature) {\n    throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature is missing')\n  } else if (!isBase64(serverSignature)) {\n    throw new Error('SASL: SCRAM-SERVER-FINAL-MESSAGE: server signature must be base64')\n  }\n  return {\n    serverSignature,\n  }\n}\n\nfunction xorBuffers(a, b) {\n  if (!Buffer.isBuffer(a)) {\n    throw new TypeError('first argument must be a Buffer')\n  }\n  if (!Buffer.isBuffer(b)) {\n    throw new TypeError('second argument must be a Buffer')\n  }\n  if (a.length !== b.length) {\n    throw new Error('Buffer lengths must match')\n  }","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L214-L250","documentation":"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.","triggerScenarios":"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.","commonSituations":"Frame truncation by a pooler/proxy; non-PostgreSQL server; intermediary rewriting the final SASL message.","solutions":["Connect directly to PostgreSQL bypassing any pooler/proxy.","Confirm the pooler/proxy version supports SCRAM-SHA-256 passthrough end-to-end.","Inspect wire frames to verify the final message is delivered intact."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect()\n} catch (err) {\n  if (/server signature is missing/.test(err.message)) {\n    logger.error('SCRAM final frame had neither v= nor e=', { err })\n    return connectBypassingPooler()\n  }\n  throw err\n}","preventionTips":["Keep the auth path free of intermediaries that rewrite final SASL frames.","Verify pooler SCRAM-SHA-256 end-to-end support.","Capture the full final frame when this fires to confirm truncation."],"tags":["sasl","scram","authentication","connection"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}