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

  1. Always call setPluginListener(listener) before adding the plugin to Matrix
  2. Use Matrix.Builder to construct plugins so listener wiring is enforced
  3. Null-check the listener before destroy() in your plugin management code
  4. 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

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


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)