sidorares/node-mysql2 · error · Error
"user" connection config property must be a string
Error message
"user" connection config property must be a string
What it means
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.
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.
Example fix
// before
createConnection({ user: 12345, password: process.env.DB_PASS });
// after
createConnection({ user: String(12345), password: process.env.DB_PASS }); Defensive patterns
Strategy: type-guard
Validate before calling
function normalizeConnectionConfig(opts) {
if ('user' in opts && opts.user != null && typeof opts.user !== 'string') {
opts = { ...opts, user: String(opts.user) };
}
return opts;
}
const safeOpts = normalizeConnectionConfig(rawOpts);
const conn = await createConnection(safeOpts); Type guard
function isValidUser(opt) {
return opt == null || typeof opt === 'string';
}
if (!isValidUser(opts.user)) {
throw new TypeError(`user must be a string, got ${typeof opts.user}`);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- "database" connection config property must be a string
- HandshakeResponse authPluginName must be a string when…
- HandshakeResponse authToken must be a Buffer when provided
- AuthPluginMoreData received but no auth plugin instance…
- Bind parameters must be array if namedPlaceholders…
AI-assisted analysis of sidorares/node-mysql2@8b1f829d37 (2026-08-11).
Data as JSON: /api/errors/a8a2e3c5695dc44d.
Report an issue: GitHub.
Appendix: source
Thrown at lib/packets/handshake_response.js:126
connectAttributes[attrNames[k]],
encoding
);
}
packet.writeLengthCodedNumber(keysLength);
for (k = 0; k < attrNames.length; ++k) {
packet.writeLengthCodedString(attrNames[k], encoding);
packet.writeLengthCodedString(
connectAttributes[attrNames[k]],
encoding
);
}
}
return packet;
}
toPacket() {
if (typeof this.user !== 'string') {
throw new Error('"user" connection config property must be a string');
}
if (typeof this.database !== 'string') {
throw new Error('"database" connection config property must be a string');
}
// dry run: calculate resulting packet length
const p = this.serializeResponse(Packet.MockBuffer());
return this.serializeResponse(Buffer.alloc(p.offset));
}
static fromPacket(packet, serverFlags = 0xffffffff) {
const args = {};
args.clientFlags = packet.readInt32();
function isSet(flag) {
return args.clientFlags & serverFlags & ClientConstants[flag];
}
args.maxPacketSize = packet.readInt32();
args.charsetNumber = packet.readInt8();
const encoding = CharsetToEncoding[args.charsetNumber];
args.encoding = encoding;View on GitHub (pinned to 8b1f829d37)