{"id":"78ce6505edf9fd53","repo":"brianc/node-postgres","slug":"sasl-invalid-attribute-pair-entry","errorCode":null,"errorMessage":"SASL: Invalid attribute pair entry","messagePattern":"SASL: Invalid attribute pair entry","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":183,"sourceCode":" * base64-3        = 3base64-char \"=\"\n *\n * base64-2        = 2base64-char \"==\"\n *\n * base64          = *base64-4 [base64-3 / base64-2]\n */\nfunction isBase64(text) {\n  return /^(?:[a-zA-Z0-9+/]{4})*(?:[a-zA-Z0-9+/]{2}==|[a-zA-Z0-9+/]{3}=)?$/.test(text)\n}\n\nfunction parseAttributePairs(text) {\n  if (typeof text !== 'string') {\n    throw new TypeError('SASL: attribute pairs text must be a string')\n  }\n\n  return new Map(\n    text.split(',').map((attrValue) => {\n      if (!/^.=/.test(attrValue)) {\n        throw new Error('SASL: Invalid attribute pair entry')\n      }\n      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')","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L165-L201","documentation":"Thrown by parseAttributePairs (sasl.js:181-184) when a comma-separated segment of a SCRAM message does not match the expected '<letter>=' format. SCRAM attribute pairs are single-letter keys (r, s, i, v, etc.) followed by '=' and a value; a segment that lacks the '=' delimiter or has a multi-character/malformed key indicates a corrupt or non-compliant server message. The regex /^.=/ requires exactly one char then '=' at the start of each segment.","triggerScenarios":"A SCRAM server message containing a segment like 'rx' (no '=') or 'nonce=abc' (multi-char key). This is parsed from the serverData string in either parseServerFirstMessage or parseServerFinalMessage.","commonSituations":"A non-compliant PostgreSQL-compatible server or proxy injecting malformed SCRAM attributes. Data corruption on the wire. Extremely rare with genuine PostgreSQL backends, as their SCRAM messages are well-formed.","solutions":["Verify you are connecting to a genuine PostgreSQL server with a compliant SCRAM implementation.","If behind a proxy/pooler, ensure it forwards SCRAM messages unmodified.","Enable SSL to prevent wire-level corruption or tampering."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect();\n} catch (err) {\n  if (/Invalid attribute pair entry/i.test(err.message)) {\n    console.error('Malformed SCRAM message from server — check for non-compliant proxy or corruption.');\n  }\n  throw err;\n}","preventionTips":["Ensure a direct or properly proxied connection to a compliant PostgreSQL server.","Use SSL to prevent wire-level corruption of SCRAM messages.","Avoid custom PostgreSQL-compatible servers unless their SCRAM implementation is RFC 5802-compliant."],"tags":["sasl","protocol","validation","authentication"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}