{"record":{"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":"validation","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/8b1f829d3706404ab372cf97bd77ebcf86578d97/lib/packets/handshake_response.js#L16-L52","documentation":"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.","triggerScenarios":"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).","commonSituations":"Custom auth plugin development where the name is computed non-string; mis-typed config from a generic helper.","solutions":["Provide authPluginName as a plain string: authPluginName: 'caching_sha2_password'.","Omit both authToken and authPluginName to use the legacy fallback path.","Validate typeof authPluginName === 'string' before constructing."],"exampleFix":"// before\nnew HandshakeResponse({ ..., authToken: buf, authPluginName: 1 });\n\n// after\nnew HandshakeResponse({ ..., authToken: buf, authPluginName: 'mysql_native_password' });","handlingStrategy":"type-guard","validationCode":"if (authPluginName !== undefined && typeof authPluginName !== 'string') {\n  throw new TypeError('authPluginName must be a string');\n}","typeGuard":"const isPluginNameString = (v) => v === undefined || typeof v === 'string';","tryCatchPattern":null,"preventionTips":["Keep authPluginName as a plain string constant per plugin.","Type the HandshakeResponse options strictly when integrating custom auth."],"tags":["authentication","handshake","serialization","validation"],"backgroundTag":null,"analyzedSha":"8b1f829d3706404ab372cf97bd77ebcf86578d97","analyzedAt":"2026-08-11T02:54:28.964Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}