sidorares/node-mysql2 · error · TypeError

HandshakeResponse authToken must be a Buffer when provided

Error message

HandshakeResponse authToken must be a Buffer when provided

What it means

HandshakeResponse constructor (lib/packets/handshake_response.js:23-32) accepts an optional pre-computed authToken for fast-path auth optimization, but when BOTH authToken and authPluginName are provided it requires authToken to be a Buffer (because it is written as raw bytes at serializeResponse line 80-88). Passing any non-Buffer throws TypeError to fail fast before wire serialization.

Solutions

  1. Provide authToken as a Buffer: authToken: Buffer.from(token, ...).
  2. If you only have a password, omit authToken/authPluginName and let the constructor fall back to mysql_native_password token calculation (line 40-58).
  3. Validate Buffer.isBuffer(authToken) before constructing HandshakeResponse.

Example fix

// before
new HandshakeResponse({ ..., authToken: 'abc', authPluginName: 'x' });

// after
new HandshakeResponse({ ..., authToken: Buffer.from('abc'), authPluginName: 'x' });
Defensive patterns

Strategy: type-guard

Validate before calling

if (authToken !== undefined && !Buffer.isBuffer(authToken)) {
  throw new TypeError('authToken must be a Buffer');
}

Type guard

const isAuthTokenBuffer = (v) => v === undefined || Buffer.isBuffer(v);

Prevention

When it happens

Trigger: Constructing HandshakeResponse manually with authToken as a string or number; an auth-plugin integration returning a string token where a Buffer is required; a custom handshake wrapper that computes a token but forgets Buffer.from(...).

Common situations: Custom auth plugin development; monkey-patching the initial handshake; integration code that confuses the legacy password-string path with the pre-computed-token path.

Understand the failure class

Related errors


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

Appendix: source

Thrown at lib/packets/handshake_response.js:29

    this.user = handshake.user || '';
    this.database = handshake.database || '';
    this.password = handshake.password || '';
    this.passwordSha1 = handshake.passwordSha1;
    this.authPluginData1 = handshake.authPluginData1;
    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,

View on GitHub (pinned to 8b1f829d37)