Tencent/matrix · error · RuntimeException
plugin destroy, plugin listener is null
Error message
plugin destroy, plugin listener is null
What it means
Plugin.destroy() throws this when pluginListener is null at destroy time (after the destroyed-state check passes). The listener's onDestroy callback must be invoked, and without one Matrix cannot complete destruction, so it throws a RuntimeException.
Solutions
- Always call setPluginListener(listener) before adding the plugin to Matrix
- Use Matrix.Builder to construct plugins so listener wiring is enforced
- Null-check the listener before destroy() in your plugin management code
- Provide a no-op PluginListener default instead of leaving it null
Example fix
// before
MyPlugin plugin = new MyPlugin();
plugin.destroy();
// after
MyPlugin plugin = new MyPlugin();
plugin.setPluginListener(new PluginListener() { /* no-op impls */ });
plugin.destroy(); Defensive patterns
Strategy: validation
Validate before calling
if (plugin.getPluginListener() == null) {
throw new IllegalStateException("plugin listener must be set before destroy");
}
plugin.destroy(); Type guard
if (plugin != null && plugin.getPluginListener() != null && !plugin.isPluginDestroyed()) { plugin.destroy(); } Try / catch
try {
plugin.destroy();
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("listener is null")) {
Log.w(TAG, "destroy skipped: no listener attached", e);
} else { throw e; }
} Prevention
- Attach a PluginListener (even a no-op) at plugin construction time
- Keep listener registration unconditional, not build-type dependent
- Add an assertion in debug builds that listeners are wired before lifecycle calls
When it happens
Trigger: Calling destroy() on a plugin that never received a non-null listener via setPluginListener(), where the plugin was also never started (so the stop()-path listener check never fired).
Common situations: Constructing plugins without registering PluginListener; custom plugin implementations forgetting to wire the listener; listener registration failing silently or being conditional on debug builds while destroy runs in all builds.
Related errors
- plugin stop, plugin listener is null
- plugin destroy, but plugin has been already destroyed
- publish issue, but issue listener is null
- plugin start, but plugin has been already destroyed
- plugin start, but plugin has been already started
AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08).
Data as JSON: /api/errors/5aed98e78db67015.
Report an issue: GitHub.
Appendix: source
Thrown at matrix/matrix-android/matrix-android-lib/src/main/java/com/tencent/matrix/plugin/Plugin.java:143
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");
}
status = PLUGIN_DESTROYED;
if (pluginListener == null) {
throw new RuntimeException("plugin destroy, plugin listener is null");
}
pluginListener.onDestroy(this);
}
@Override
public String getTag() {
return getClass().getName();
}
@Override
public void onForeground(boolean isForeground) {
}
public boolean isForeground() {
return ProcessUILifecycleOwner.INSTANCE.isProcessForeground();
}
View on GitHub (pinned to 3b8293bd65)