{"id":"4b24685f9406ef7a","repo":"sidorares/node-mysql2","slug":"handshakeresponse-authpluginname-must-be-a-string","errorCode":null,"errorMessage":"HandshakeResponse authPluginName must be a string when provided","messagePattern":"HandshakeResponse authPluginName must be a string when provided","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/packets/handshake_response.js","lineNumber":34,"sourceCode":"    this.authPluginData2 = handshake.authPluginData2;\n    this.compress = handshake.compress;\n    this.clientFlags = handshake.flags;\n    this.mariadbExtendedClientFlags = handshake.mariadbExtendedClientFlags || 0;\n\n    // Accept pre-calculated authToken and authPluginName from caller\n    // This allows the caller to optimize by using the server's preferred auth method\n    if (\n      handshake.authToken !== undefined &&\n      handshake.authPluginName !== undefined\n    ) {\n      // Validate types to fail fast with clear errors\n      if (!Buffer.isBuffer(handshake.authToken)) {\n        throw new TypeError(\n          'HandshakeResponse authToken must be a Buffer when provided'\n        );\n      }\n      if (typeof handshake.authPluginName !== 'string') {\n        throw new TypeError(\n          'HandshakeResponse authPluginName must be a string when provided'\n        );\n      }\n      this.authToken = handshake.authToken;\n      this.authPluginName = handshake.authPluginName;\n    } else {\n      // Fallback to legacy behavior: calculate mysql_native_password token\n      // TODO: pre-4.1 auth support\n      let authToken;\n      if (this.passwordSha1) {\n        authToken = auth41.calculateTokenFromPasswordSha(\n          this.passwordSha1,\n          this.authPluginData1,\n          this.authPluginData2\n        );\n      } else {\n        authToken = auth41.calculateToken(\n          this.password,","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/sidorares/node-mysql2/blob/5ebe8903d6aea2d8ea1490e11b52491526e50f19/lib/packets/handshake_response.js#L16-L52","documentation":"When a caller supplies both `authToken` and `authPluginName` to HandshakeResponse (the optimised pre-calculated-token path), `authPluginName` must be a string. If it is present but non-string, HandshakeResponse throws at construction. Standard connection setup never sets these fields, so this is only relevant to advanced/internal callers.","triggerScenarios":"Constructing HandshakeResponse with `authPluginName` set to a non-string (number, object, Buffer) alongside an `authToken`. Happens in custom handshake code or tests that build the packet manually.","commonSituations":"A fork or test harness passing the plugin name from an unvalidated source; confusing authPluginName (string) with authToken (Buffer).","solutions":["Ensure authPluginName is a string: `new HandshakeResponse({ ..., authToken, authPluginName: String(name) })`.","Omit both fields if you do not need the optimised path."],"exampleFix":"// before\nnew HandshakeResponse({ ..., authToken, authPluginName: 42 });\n\n// after\nnew HandshakeResponse({ ..., authToken, authPluginName: 'caching_sha2_password' });","handlingStrategy":"type-guard","validationCode":"function buildHandshakeOpts(opts) {\n  if (opts.authPluginName !== undefined && typeof opts.authPluginName !== 'string') {\n    throw new TypeError('authPluginName must be a string');\n  }\n  return opts;\n}","typeGuard":"function isValidAuthPluginName(name) {\n  return name == null || typeof name === 'string';\n}","tryCatchPattern":null,"preventionTips":["Omit authToken/authPluginName unless you are providing a pre-calculated token.","Coerce plugin names with String(...) when sourced from dynamic input.","Keep direct HandshakeResponse use in tests/internal code."],"tags":["auth","handshake","internal"],"analyzedSha":"5ebe8903d6aea2d8ea1490e11b52491526e50f19","analyzedAt":"2026-08-03T18:58:53.602Z","schemaVersion":2}