{"id":"d937c0fc42d79f8a","repo":"sidorares/node-mysql2","slug":"authpluginmoredata-received-but-no-auth-plugin-ins","errorCode":null,"errorMessage":"AuthPluginMoreData received but no auth plugin instance found","messagePattern":"AuthPluginMoreData received but no auth plugin instance found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/commands/auth_switch.js","lineNumber":131,"sourceCode":"}\n\nfunction authSwitchRequestMoreData(packet, connection, command) {\n  const { data } = Packets.AuthSwitchRequestMoreData.fromPacket(packet);\n\n  if (connection.config.authSwitchHandler) {\n    const legacySwitchHandler = connection.config.authSwitchHandler;\n    warnLegacyAuthSwitch();\n    legacySwitchHandler({ pluginData: data }, (err, data) => {\n      if (err) {\n        return authSwitchPluginError(err, command);\n      }\n      connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());\n    });\n    return;\n  }\n\n  if (!connection._authPlugin) {\n    throw new Error(\n      'AuthPluginMoreData received but no auth plugin instance found'\n    );\n  }\n  Promise.resolve(connection._authPlugin(data))\n    .then((data) => {\n      if (data) {\n        connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());\n      }\n    })\n    .catch((err) => {\n      authSwitchPluginError(err, command);\n    });\n}\n\nmodule.exports = {\n  authSwitchRequest,\n  authSwitchRequestMoreData,\n  getAuthPlugin,","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/sidorares/node-mysql2/blob/5ebe8903d6aea2d8ea1490e11b52491526e50f19/lib/commands/auth_switch.js#L113-L149","documentation":"The server sent an AuthSwitchRequestMoreData packet but `connection._authPlugin` was never set — meaning no prior auth-switch request established a plugin instance to feed the data to. This is a protocol desync: the server jumped ahead in the conversation, or a previous plugin threw and left the connection in a half-initialised state.","triggerScenarios":"The server sends MoreData before (or without) a prior AuthSwitchRequest that would have instantiated the plugin; or the plugin factory threw during instantiation and `_authPlugin` stayed unset while packets kept arriving. Seen with proxies that reorder auth packets, or after a prior auth error was partially suppressed.","commonSituations":"A buggy proxy reordering/duplicating auth packets; a custom authPlugins factory that throws instead of returning a function; connecting through a pooler that hands off a half-authenticated socket.","solutions":["Connect directly to mysqld, bypassing any proxy/pooler, to isolate the source.","If using a custom `authPlugins` entry, ensure its factory returns a function and never throws during construction.","Update/upgrade the proxy and mysql2.","Enable TLS so intermediaries are less likely to interfere with the auth sequence."],"exampleFix":"// before — custom plugin factory throws\nauthPlugins: { my_plugin: () => { throw new Error('bad'); } }\n\n// after — factory returns a handler function\nauthPlugins: {\n  my_plugin: (opts) => ({ connection }) => (data) => {\n    return Buffer.from('response');\n  },\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await mysql.createConnection(cfg);\n} catch (err) {\n  if (/AuthPluginMoreData received but no auth plugin instance/.test(err.message)) {\n    // bypass proxy; verify custom authPlugins factory does not throw\n  } else throw err;\n}","preventionTips":["Ensure custom authPlugins factories always return a handler function and never throw on construction.","Avoid proxies that reorder auth packets.","Connect directly to mysqld when isolating auth issues."],"tags":["auth","protocol","connection","proxy"],"analyzedSha":"5ebe8903d6aea2d8ea1490e11b52491526e50f19","analyzedAt":"2026-08-03T18:58:53.602Z","schemaVersion":2}