{"id":"c3f2effd2fe1d3dd","repo":"mongodb/node-mongodb-native","slug":"username-must-be-a-string","errorCode":null,"errorMessage":"Username must be a string","messagePattern":"Username must be a string","errorType":"exception","errorClass":"MongoInvalidArgumentError","httpStatus":null,"severity":"error","filePath":"src/cmap/auth/scram.ts","lineNumber":219,"sourceCode":"  };\n\n  await connection.command(ns(`${db}.$cmd`), retrySaslContinueCmd, undefined);\n}\n\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',","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/mongodb/node-mongodb-native/blob/3366c21a6311e02f1be91da982f9b93d3cce99a0/src/cmap/auth/scram.ts#L201-L237","documentation":"Thrown by passwordDigest() (scram.ts:219) as a MongoInvalidArgumentError when the username passed in is not a string. passwordDigest builds the MD5 of 'username:mongo:password' for SCRAM-SHA-1, so a non-string username (number, object, undefined) cannot be hashed. This guards an internal helper invoked during SCRAM-SHA-1 auth.","triggerScenarios":"Credentials object whose username field is not a string (e.g. a number parsed from config without coercion, or undefined leaking through a loosely-typed credentials builder). Reachable if a MongoCredentials is constructed bypassing its normal validation.","commonSituations":"Numeric usernames in config systems that did not stringify; a credentials provider returning an object whose .username is undefined after a failed lookup; TS type-safety bypassed at runtime.","solutions":["Ensure the username is a string - coerce with String(username) if loading from typed config","Validate credentials shape before passing to MongoClient","Use SCRAM-SHA-256 (default in modern MongoDB) where possible"],"exampleFix":"// before\nconst username = config.dbUser; // number 12345\n// after\nconst username = String(config.dbUser);","handlingStrategy":"validation","validationCode":"function assertStringUsername(username: unknown): asserts username is string {\n  if (typeof username !== 'string') throw new TypeError('username must be a string');\n}\nassertStringUsername(config.dbUser);","typeGuard":"function isStringUsername(u: unknown): u is string {\n  return typeof u === 'string';\n}","tryCatchPattern":null,"preventionTips":["Coerce config values to string before passing to MongoClient","Validate credentials shape at app startup","Use TypeScript types strictly; avoid 'any' for credential fields"],"tags":["auth","scram","credentials","validation"],"analyzedSha":"3366c21a6311e02f1be91da982f9b93d3cce99a0","analyzedAt":"2026-08-04T13:40:15.335Z","schemaVersion":2}