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

  1. Check before installing: if (!holder.isInstalledPlugin(MyPlugin.class)) holder.installPlugin(new MyPlugin())
  2. If the plugin is auto-installed by a starter, remove the manual installPlugin call entirely
  3. 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

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.