{"id":"91f8b3cb0cd8ee74","repo":"brianc/node-postgres","slug":"sasl-scram-server-first-message-salt-missing","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing","messagePattern":"SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":203,"sourceCode":"      const name = attrValue[0]\n      const value = attrValue.substring(2)\n      return [name, value]\n    })\n  )\n}\n\nfunction parseServerFirstMessage(data) {\n  const attrPairs = parseAttributePairs(data)\n\n  const nonce = attrPairs.get('r')\n  if (!nonce) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce missing')\n  } else if (!isPrintableChars(nonce)) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: nonce must only contain printable characters')\n  }\n  const salt = attrPairs.get('s')\n  if (!salt) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt missing')\n  } else if (!isBase64(salt)) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64')\n  }\n  const iterationText = attrPairs.get('i')\n  if (!iterationText) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing')\n  } else if (!/^[1-9][0-9]*$/.test(iterationText)) {\n    throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count')\n  }\n  const iteration = parseInt(iterationText, 10)\n\n  return {\n    nonce,\n    salt,\n    iteration,\n  }\n}\n","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L185-L221","documentation":"Thrown by parseServerFirstMessage (sasl.js:201-203) when the server's first SCRAM message has no 's' attribute (the salt). The salt is mandatory per RFC 5802 §5.1; it is used as input to PBKDF2 for key derivation. Its absence means the client cannot derive the salted password and the authentication exchange is incomplete. The code reads attrPairs.get('s') and throws if falsy.","triggerScenarios":"The server's AuthenticationSASLContinue payload lacks the s=<salt> attribute. Parsed from serverData in continueSession -> parseServerFirstMessage.","commonSituations":"A non-compliant server sending an incomplete SCRAM first message. A proxy truncating the message. Wire-level corruption. Rare with genuine PostgreSQL backends.","solutions":["Verify you are connecting to a genuine PostgreSQL server.","Check that no intermediary (PgBouncer, load balancer) is modifying or truncating SASL messages.","Enable SSL to ensure authentication message integrity."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect();\n} catch (err) {\n  if (/salt missing/i.test(err.message)) {\n    console.error('SCRAM first message missing salt — non-compliant server or truncated message.');\n  }\n  throw err;\n}","preventionTips":["Connect to a genuine, compliant PostgreSQL server.","Check for proxies that may truncate or modify SASL messages.","Enable SSL to ensure authentication message integrity."],"tags":["sasl","protocol","validation","authentication"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}