Tencent/matrix · error · RuntimeException
plugin stop, but plugin is never started
Error message
plugin stop, but plugin is never started
What it means
Thrown from Plugin.stop() when the plugin has never reached PLUGIN_STARTED state. stop() must pair with a prior successful start(); stopping an unstarted plugin would emit an unbalanced onStop callback to the plugin listener, so the guard rejects it. Usually indicates calling stop before start or after a failed start.
Solutions
- Guard stop with plugin.isPluginStarted() before calling.
- Symmetrically pair every stop() with a guaranteed start() in the same lifecycle path.
- Track which plugins were started (e.g. a set of started tags) and only stop those.
Example fix
// before
plugin.stop();
// after
if (plugin.isPluginStarted()) {
plugin.stop();
} Defensive patterns
Strategy: type-guard
Validate before calling
if (plugin.isPluginStarted()) {
plugin.stop();
} Type guard
boolean canStop(Plugin p) { return p.isPluginStarted(); } Try / catch
try {
plugin.stop();
} catch (RuntimeException e) {
Log.w(TAG, "plugin was never started", e);
} Prevention
- Pair every stop() with a guaranteed start()
- Track started plugins before stopping them in teardown
- Skip stop() in onDestroy paths for plugins that may never have started
When it happens
Trigger: Calling plugin.stop() without a prior plugin.start(); conditional start paths where start was skipped but stop runs unconditionally.
Common situations: onDestroy/onLowMemory handlers stopping plugins that were never started; feature flags that skip start but not the paired stop; asymmetric start/stop calls in lifecycle code.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- plugin start, but plugin has been already destroyed
- plugin start, but plugin has been already started
- plugin start, plugin listener is null
- plugin stop, but plugin has been already destroyed
- plugin with tag is already exist
AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08).
Data as JSON: /api/errors/ba92f6ed2c249aed.
Report an issue: GitHub.
Appendix: source
Thrown at matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/plugin/Plugin.java:120
throw new RuntimeException("plugin start, but plugin has been already started");
}
status = PLUGIN_STARTED;
if (pluginListener == null) {
throw new RuntimeException("plugin start, plugin listener is null");
}
pluginListener.onStart(this);
}
@Override
public void stop() {
if (isPluginDestroyed()) {
throw new RuntimeException("plugin stop, but plugin has been already destroyed");
}
if (!isPluginStarted()) {
throw new RuntimeException("plugin stop, but plugin is never started");
}
status = PLUGIN_STOPPED;
if (pluginListener == null) {
throw new RuntimeException("plugin stop, plugin listener is null");
}
pluginListener.onStop(this);
}
@Override
public void destroy() {
// stop first
if (isPluginStarted()) {
stop();
}
if (isPluginDestroyed()) {
throw new RuntimeException("plugin destroy, but plugin has been already destroyed");View on GitHub (pinned to 3b8293bd65)