{"record":{"id":"6858bd8e95450e54","repo":"brianc/node-postgres","slug":"sasl-scram-server-first-message-salt-must-be-bas","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64","messagePattern":"SASL: SCRAM-SERVER-FIRST-MESSAGE: salt must be base64","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":205,"sourceCode":"      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\nfunction parseServerFinalMessage(serverData) {\n  const attrPairs = parseAttributePairs(serverData)","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/brianc/node-postgres/blob/ff9d775abd12f29dd6df03945253b54eabbb29f2/packages/pg/lib/crypto/sasl.js#L187-L223","documentation":"Thrown by parseServerFirstMessage() when the salt value from the s= attribute fails the isBase64() regex check. The salt must be valid standard base64 (alphabet A-Z, a-z, 0-9, +, / with correct = padding). Invalid base64 means the client cannot decode the salt bytes for PBKDF2.","triggerScenarios":"At sasl.js:204-205, isBase64(salt) returns false. The s= attribute value does not match the regex /^(?:[a-zA-Z0-9+/]{4})*(?:[a-zA-Z0-9+/]{2}==|[a-zA-Z0-9+/]{3}=)?$/ — it contains invalid characters, incorrect padding, or a length not divisible by 4.","commonSituations":"Data corruption in transit altering salt bytes; an encoding mismatch (e.g., URL-safe base64 with - and _ instead of + and /); a non-conformant server sending raw hex or another format; a proxy re-encoding the authentication stream.","solutions":["Verify network integrity — check for corrupting intermediaries.","Confirm the server is a standard PostgreSQL instance.","Enable SSL/TLS to protect the authentication data stream.","Test the connection with psql from the same environment."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect()\n} catch (err) {\n  if (err.message.includes('salt must be base64')) {\n    throw new Error('SCRAM salt is not valid base64 — check for data corruption or non-conformant server')\n  }\n  throw err\n}","preventionTips":["Verify network integrity — check for corrupting proxies or encoding issues.","Confirm the server is a standard PostgreSQL instance.","Enable SSL/TLS to protect authentication data.","Test with psql from the same environment to isolate server vs. client issues."],"tags":["authentication","sasl","scram","protocol-error","base64","data-integrity"],"backgroundTag":null,"analyzedSha":"ff9d775abd12f29dd6df03945253b54eabbb29f2","analyzedAt":"2026-08-11T15:33:59.644Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}