{"record":{"id":"a8a2e3c5695dc44d","repo":"sidorares/node-mysql2","slug":"user-connection-config-property-must-be-a-string-a8a2e3","errorCode":null,"errorMessage":"\"user\" connection config property must be a string","messagePattern":"\"user\" connection config property must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/packets/handshake_response.js","lineNumber":126,"sourceCode":"          connectAttributes[attrNames[k]],\n          encoding\n        );\n      }\n      packet.writeLengthCodedNumber(keysLength);\n      for (k = 0; k < attrNames.length; ++k) {\n        packet.writeLengthCodedString(attrNames[k], encoding);\n        packet.writeLengthCodedString(\n          connectAttributes[attrNames[k]],\n          encoding\n        );\n      }\n    }\n    return packet;\n  }\n\n  toPacket() {\n    if (typeof this.user !== 'string') {\n      throw new Error('\"user\" connection config property must be a string');\n    }\n    if (typeof this.database !== 'string') {\n      throw new Error('\"database\" connection config property must be a string');\n    }\n    // dry run: calculate resulting packet length\n    const p = this.serializeResponse(Packet.MockBuffer());\n    return this.serializeResponse(Buffer.alloc(p.offset));\n  }\n  static fromPacket(packet, serverFlags = 0xffffffff) {\n    const args = {};\n    args.clientFlags = packet.readInt32();\n    function isSet(flag) {\n      return args.clientFlags & serverFlags & ClientConstants[flag];\n    }\n    args.maxPacketSize = packet.readInt32();\n    args.charsetNumber = packet.readInt8();\n    const encoding = CharsetToEncoding[args.charsetNumber];\n    args.encoding = encoding;","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/sidorares/node-mysql2/blob/8b1f829d3706404ab372cf97bd77ebcf86578d97/lib/packets/handshake_response.js#L108-L144","documentation":"Thrown by HandshakeResponse.toPacket() while building the authentication packet sent during connection establishment. The constructor sets this.user = handshake.user || '', which only coerces falsy values to '' — a truthy non-string (number, object, array) passes through the constructor but fails this typeof check at packet serialization time. Because toPacket() runs during the live handshake (not at config validation), the error surfaces late, after a socket is already open.","triggerScenarios":"Passing a truthy non-string as `user` in the connection config: createConnection({ user: 12345 }), createPool({ user: process.env.DB_USER * 1 }), or createConnection({ user: { name: 'root' } }). The check fires the moment the server handshake completes and the client sends its HandshakeResponse.","commonSituations":"All-numeric MySQL usernames loaded from typed config/JSON where the value was not quoted; TypeORM/Sequelize configs that pass user as a number; reading user from an env var cast with Number(); config builders that forward untyped values.","solutions":["Cast the value to a string at the call site: user: String(opts.user) or user: '' + opts.user.","Validate the full config object before createConnection/createPool so type errors fail fast and early.","Fix the upstream config source (JSON quoting, env parsing) so `user` is always a string."],"exampleFix":"// before\ncreateConnection({ user: 12345, password: process.env.DB_PASS });\n// after\ncreateConnection({ user: String(12345), password: process.env.DB_PASS });","handlingStrategy":"type-guard","validationCode":"function normalizeConnectionConfig(opts) {\n  if ('user' in opts && opts.user != null && typeof opts.user !== 'string') {\n    opts = { ...opts, user: String(opts.user) };\n  }\n  return opts;\n}\n\nconst safeOpts = normalizeConnectionConfig(rawOpts);\nconst conn = await createConnection(safeOpts);","typeGuard":"function isValidUser(opt) {\n  return opt == null || typeof opt === 'string';\n}\n\nif (!isValidUser(opts.user)) {\n  throw new TypeError(`user must be a string, got ${typeof opts.user}`);\n}","tryCatchPattern":null,"preventionTips":["Validate the entire config object (host, user, password, database) with a schema (zod/joi) before passing it to createConnection/createPool.","Always read credentials from env as strings: process.env.DB_USER (not Number(process.env.DB_USER)).","Keep config values typed as string in JSON/YAML by quoting numeric usernames."],"tags":["config","authentication","validation","type-mismatch"],"backgroundTag":null,"analyzedSha":"8b1f829d3706404ab372cf97bd77ebcf86578d97","analyzedAt":"2026-08-11T02:54:28.964Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}