{"id":"90484998f8ecca96","repo":"brianc/node-postgres","slug":"sasl-scram-server-first-message-iteration-missin","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing","messagePattern":"SASL: SCRAM-SERVER-FIRST-MESSAGE: iteration missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":209,"sourceCode":"\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)\n  const error = attrPairs.get('e')\n  const serverSignature = attrPairs.get('v')\n\n  if (error) {","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L191-L227","documentation":"Thrown by parseServerFirstMessage when the server's first SCRAM message lacks the required `i=` (iteration count) attribute entirely (sasl.js:208, iterationText is falsy). RFC 5802 mandates this attribute, so its absence indicates a non-conformant or truncated server response.","triggerScenarios":"continueSession receives serverData with no `i=...` pair in the comma-separated attribute list produced by parseAttributePairs (sasl.js:175). attrPairs.get('i') returns undefined.","commonSituations":"Same proxy/pooler frame-mangling family as the salt error; a NAT/MITM device truncating the auth exchange; a server implementing an outdated or non-standard SASL profile.","solutions":["Connect directly to PostgreSQL bypassing any pooler/proxy.","Confirm the pooler/proxy version supports SCRAM-SHA-256 passthrough.","Verify TLS integrity end-to-end (no frame-rewriting middlebox)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect()\n} catch (err) {\n  if (/iteration missing/.test(err.message)) {\n    logger.error('SCRAM final frame missing i= attribute -- likely intermediary truncation', { err })\n    return connectDirectlyToPrimary()\n  }\n  throw err\n}","preventionTips":["Avoid SCRAM-incompatible poolers in the auth path.","Use direct TLS to Postgres to keep SASL frames intact.","Smoke-test connect() against the primary in CI to surface intermediary issues early."],"tags":["sasl","scram","authentication","connection"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}