dromara/Sa-Token · error · SaTokenPluginException
插件 [ " + plugin.getClass().getCanonicalName() + " ] 已安装,不可重复
Error message
插件 [ " + plugin.getClass().getCanonicalName() + " ] 已安装,不可重复安装
What it means
SaTokenPluginException thrown by SaTokenPluginHolder.installPlugin(SaTokenPlugin) when a plugin of the same class (isInstalledPlugin(plugin.getClass())) is already installed. Sa-Token enforces one instance per plugin class to keep install/destroy hook bookkeeping deterministic; duplicate installation is rejected with a message naming the offending class's canonical name.
Source
Thrown at sa-token-core/src/main/java/cn/dev33/satoken/plugin/SaTokenPluginHolder.java:203
}
// ------------------- 插件 Install 与 Destroy -------------------
/**
* 安装指定插件
* @param plugin /
*/
public synchronized SaTokenPluginHolder installPlugin(SaTokenPlugin plugin) {
// 插件为空,拒绝安装
if (plugin == null) {
throw new SaTokenPluginException("插件不可为空");
}
// 插件已经被安装过了,拒绝再次安装
if (isInstalledPlugin(plugin.getClass())) {
throw new SaTokenPluginException("插件 [ " + plugin.getClass().getCanonicalName() + " ] 已安装,不可重复安装");
}
// 执行该插件的 install 前置钩子
_consumeHooks(beforeInstallHooks, plugin.getClass());
// 插件安装
int consumeCount = _consumeHooks(installHooks, plugin.getClass());
if (consumeCount == 0) {
plugin.install();
}
// 执行该插件的 install 后置钩子
_consumeHooks(afterInstallHooks, plugin.getClass());
// 添加到插件集合
pluginList.add(plugin);
// 返回对象自身,支持连缀风格调用View on GitHub (pinned to ac2c7f6e94)
Solutions
- Check before installing: if (!holder.isInstalledPlugin(MyPlugin.class)) holder.installPlugin(new MyPlugin())
- If the plugin is auto-installed by a starter, remove the manual installPlugin call entirely
- For hot-reload scenarios, call destroyPlugin first (or reset the holder) before re-installing
Example fix
// before
SaManager.getSaTokenPluginHolder().installPlugin(new SaSsoPlugin()); // starter already installed it -> exception
// after
SaTokenPluginHolder holder = SaManager.getSaTokenPluginHolder();
if (!holder.isInstalledPlugin(SaSsoPlugin.class)) {
holder.installPlugin(new SaSsoPlugin());
} Defensive patterns
Strategy: validation
Validate before calling
SaTokenPluginHolder holder = SaManager.getSaTokenPluginHolder();
if (!holder.isInstalledPlugin(MyPlugin.class)) {
holder.installPlugin(new MyPlugin());
} Prevention
- Know which plugins your starter auto-installs before adding manual installPlugin calls
- Wrap installPlugin with an isInstalledPlugin(Class) guard in any code that may re-run
- In devtools/hot-reload setups, destroy plugins before re-installing them
When it happens
Trigger: Calling installPlugin twice with the same plugin class — including two different instances of the same class (the check is by Class, not by identity). Common in code that re-runs setup, or when both manual registration and an auto-configuration/starter path install the same plugin.
Common situations: Manually installing a plugin (e.g. sa-sso, sa-temp) that the starter already auto-installed; app code that installs plugins on each re-initialization (hot reload/devtools restart); copying example setup code into a project that already configures the plugin.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/6df7dbe301878c28.
Report an issue: GitHub.