sidorares/node-mysql2 · error · TypeError

HandshakeResponse authPluginName must be a string when…

Error message

HandshakeResponse authPluginName must be a string when provided

What it means

HandshakeResponse constructor (lib/packets/handshake_response.js:33-37), in the same pre-computed-token branch as error [18], requires authPluginName to be a string because it is written null-terminated at serializeResponse line 96-99. A non-string plugin name corrupts the wire packet, so the driver throws TypeError when both fields are provided.

Solutions

  1. Provide authPluginName as a plain string: authPluginName: 'caching_sha2_password'.
  2. Omit both authToken and authPluginName to use the legacy fallback path.
  3. Validate typeof authPluginName === 'string' before constructing.

Example fix

// before
new HandshakeResponse({ ..., authToken: buf, authPluginName: 1 });

// after
new HandshakeResponse({ ..., authToken: buf, authPluginName: 'mysql_native_password' });
Defensive patterns

Strategy: type-guard

Validate before calling

if (authPluginName !== undefined && typeof authPluginName !== 'string') {
  throw new TypeError('authPluginName must be a string');
}

Type guard

const isPluginNameString = (v) => v === undefined || typeof v === 'string';

Prevention

When it happens

Trigger: Passing authPluginName as a Buffer or object alongside an authToken; a plugin integration returning the name in the wrong shape; copy-paste confusing authToken (Buffer) with authPluginName (string).

Common situations: Custom auth plugin development where the name is computed non-string; mis-typed config from a generic helper.

Understand the failure class

Related errors


AI-assisted analysis of sidorares/node-mysql2@8b1f829d37 (2026-08-11). Data as JSON: /api/errors/4b24685f9406ef7a. Report an issue: GitHub.

Appendix: source

Thrown at lib/packets/handshake_response.js:34

    this.authPluginData2 = handshake.authPluginData2;
    this.compress = handshake.compress;
    this.clientFlags = handshake.flags;
    this.mariadbExtendedClientFlags = handshake.mariadbExtendedClientFlags || 0;

    // Accept pre-calculated authToken and authPluginName from caller
    // This allows the caller to optimize by using the server's preferred auth method
    if (
      handshake.authToken !== undefined &&
      handshake.authPluginName !== undefined
    ) {
      // Validate types to fail fast with clear errors
      if (!Buffer.isBuffer(handshake.authToken)) {
        throw new TypeError(
          'HandshakeResponse authToken must be a Buffer when provided'
        );
      }
      if (typeof handshake.authPluginName !== 'string') {
        throw new TypeError(
          'HandshakeResponse authPluginName must be a string when provided'
        );
      }
      this.authToken = handshake.authToken;
      this.authPluginName = handshake.authPluginName;
    } else {
      // Fallback to legacy behavior: calculate mysql_native_password token
      // TODO: pre-4.1 auth support
      let authToken;
      if (this.passwordSha1) {
        authToken = auth41.calculateTokenFromPasswordSha(
          this.passwordSha1,
          this.authPluginData1,
          this.authPluginData2
        );
      } else {
        authToken = auth41.calculateToken(
          this.password,

View on GitHub (pinned to 8b1f829d37)