{"id":"b2914ba2d36e8d57","repo":"mongodb/node-mongodb-native","slug":"password-must-be-a-string","errorCode":null,"errorMessage":"Password must be a string","messagePattern":"Password must be a string","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/cmap/auth/scram.ts","lineNumber":223,"sourceCode":"\nfunction parsePayload(payload: Binary) {\n  const payloadStr = ByteUtils.toUTF8(payload.buffer, 0, payload.position, false);\n  const dict: Document = {};\n  const parts = payloadStr.split(',');\n  for (let i = 0; i < parts.length; i++) {\n    const valueParts = (parts[i].match(/^([^=]*)=(.*)$/) ?? []).slice(1);\n    dict[valueParts[0]] = valueParts[1];\n  }\n  return dict;\n}\n\nfunction passwordDigest(username: string, password: string) {\n  if (typeof username !== 'string') {\n    throw new MongoInvalidArgumentError('Username must be a string');\n  }\n\n  if (typeof password !== 'string') {\n    throw new MongoInvalidArgumentError('Password must be a string');\n  }\n\n  if (password.length === 0) {\n    throw new MongoInvalidArgumentError('Password cannot be empty');\n  }\n\n  let nodeCrypto;\n  try {\n    // TODO: NODE-7424 - remove dependency on 'crypto' for SCRAM-SHA-1 authentication\n    // eslint-disable-next-line @typescript-eslint/no-require-imports\n    nodeCrypto = require('crypto');\n  } catch (e) {\n    throw new MongoRuntimeError(\n      'Node.js crypto module is required for SCRAM-SHA-1 authentication',\n      {\n        cause: e\n      }\n    );","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/cmap/auth/scram.ts#L205-L241","documentation":"Thrown by passwordDigest() (scram.ts:223) as a MongoInvalidArgumentError when the password is not a string. SCRAM-SHA-1 hashes the password as part of the digest; a non-string password (Buffer, number, undefined) cannot be processed. Defensive check inside the internal digest helper.","triggerScenarios":"Credentials constructed with a non-string password - e.g. a Buffer, number, or undefined leaking through a credentials resolver that bypassed MongoCredentials validation.","commonSituations":"Password loaded from a secret manager that returned a Buffer; numeric-only password not stringified; a credentials object built without going through the standard constructor.","solutions":["Coerce the password to a string before building credentials","If your secret store returns a Buffer, call .toString('utf8') first","Construct credentials through the standard MongoCredentials path so validation runs"],"exampleFix":"// before\nconst password = secretBuffer; // Buffer\n// after\nconst password = secretBuffer.toString('utf8');","handlingStrategy":"validation","validationCode":"function assertStringPassword(p: unknown): asserts p is string {\n  if (typeof p !== 'string') throw new TypeError('password must be a string');\n}\nconst password = secretBuffer?.toString('utf8');\nassertStringPassword(password);","typeGuard":"function isStringPassword(p: unknown): p is string {\n  return typeof p === 'string';\n}","tryCatchPattern":null,"preventionTips":["Convert secret-store Buffers to strings before building credentials","Type credentials fields as string, not string|Buffer","Validate credential inputs at startup, not at connect time"],"tags":["auth","scram","credentials","validation"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}