{"id":"d162a71cdbdca0a1","repo":"brianc/node-postgres","slug":"sasl-scram-server-first-message-invalid-iteratio","errorCode":null,"errorMessage":"SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count","messagePattern":"SASL: SCRAM-SERVER-FIRST-MESSAGE: invalid iteration count","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/pg/lib/crypto/sasl.js","lineNumber":211,"sourceCode":"  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) {\n    throw new Error(`SASL: SCRAM-SERVER-FINAL-MESSAGE: server returned error: \"${error}\"`)\n  }","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/brianc/node-postgres/blob/c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711/packages/pg/lib/crypto/sasl.js#L193-L229","documentation":"Thrown when the iteration count attribute is present but does not match the regex `^[1-9][0-9]*$` (sasl.js:210) — meaning it is zero, zero-prefixed, negative, or non-numeric. This is distinct from the separate upper-bound check at line 87 which is governed by scramMaxIterations.","triggerScenarios":"Server sends `i=0`, `i=-1024`, `i=04096`, `i=abc`, or any value failing the decimal regex at line 210. The iteration count is then unusable for deriveKey at line 116.","commonSituations":"Protocol corruption by an intermediary; a non-PostgreSQL server. Note: legitimate-but-excessive iteration counts hit a *different* error (line 88) governed by scramMaxIterations; this regex failure is purely about malformed syntax.","solutions":["Connect directly to PostgreSQL bypassing any pooler/proxy.","If the server is a real Postgres, inspect the wire frames (e.g. libpcap) to confirm the intermediary is not rewriting `i=`.","Note: tuning scramMaxIterations will NOT fix this — that flag only affects the upper-bound check, not the syntax check."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await client.connect()\n} catch (err) {\n  if (/invalid iteration count/.test(err.message)) {\n    // note: scramMaxIterations does NOT affect this regex check\n    logger.error('Server sent a syntactically invalid i= attribute', { err })\n    return connectBypassingPooler()\n  }\n  throw err\n}","preventionTips":["Do not conflate scramMaxIterations tuning with this error -- the flag only governs the upper-bound check at sasl.js:87.","Capture wire frames when this fires to confirm intermediary rewriting.","Keep the auth path free of middleboxes that rewrite SASL attributes."],"tags":["sasl","scram","authentication","connection"],"analyzedSha":"c5e8c9a57bff6d9160ec5dbd5c4f4c1e4c460711","analyzedAt":"2026-08-03T18:47:28.334Z","schemaVersion":2}