Flow-Launcher/Flow.Launcher · error · InvalidOperationException

Unable to find plugin: {pluginId}

Error message

Unable to find plugin: {pluginId}

What it means

Thrown by the PluginSettingsWindow constructor when PluginManager.GetPluginForId(pluginId) returns null. The guard is reached only after a non-blank pluginId check, so the ID is syntactically valid but no loaded plugin matches it. This is a programmer/wiring error rather than a runtime Shell failure: the caller asked for settings of a plugin the running Flow Launcher instance does not have loaded.

Source

Thrown at Flow.Launcher/PluginSettingsWindow.xaml.cs:31

{
    private readonly Settings _settings;

    public string PluginId { get; }

    public PluginSettingsWindow(string pluginId)
    {
        _settings = Ioc.Default.GetRequiredService<Settings>();

        if (string.IsNullOrWhiteSpace(pluginId)){
            throw new ArgumentException("Plugin ID cannot be null or whitespace.", nameof(pluginId));
        }

        PluginId = pluginId;

        var pluginPair = PluginManager.GetPluginForId(pluginId);
        if (pluginPair == null)
        {
            throw new InvalidOperationException($"Unable to find plugin: {pluginId}");
        }

        var pluginSettings = _settings.PluginSettings.GetPluginSettings(pluginId);
        if (pluginSettings == null)
        {
            throw new InvalidOperationException($"Unable to load settings for plugin: {pluginPair.Metadata.Name}");
        }

        var pluginViewModel = new PluginViewModel
        {
            PluginPair = pluginPair,
            PluginSettingsObject = pluginSettings,
            IsExpanded = true,
        };

        DataContext = pluginViewModel;
        Title = Localize.pluginSettingsWindowTitle(pluginPair.Metadata.Name);
        InitializeComponent();

View on GitHub (pinned to 7fc63b07bb)

Solutions

  1. Before constructing PluginSettingsWindow, check PluginManager.GetPluginForId(pluginId) != null and skip the row otherwise.
  2. Refresh the plugin list (PluginManager.ReloadData) before opening the window if plugins may have changed.
  3. Include the pluginId in the message (it already does) and add a friendly dialog so the user can act on it.
  4. Make PluginManager.GetPluginForId case-insensitive to defend against casing drift.

Example fix

// before
var pluginPair = PluginManager.GetPluginForId(pluginId);
if (pluginPair == null)
{
    throw new InvalidOperationException($"Unable to find plugin: {pluginId}");
}

// after - guard at the call site before opening the window
public static async Task OpenAsync(string pluginId)
{
    if (PluginManager.GetPluginForId(pluginId) is null)
    {
        App.API.ShowMsgError($"Plugin '{pluginId}' is not loaded. It may have been uninstalled.");
        return;
    }
    new PluginSettingsWindow(pluginId).Show();
}
Defensive patterns

Strategy: validation

Validate before calling

if (PluginManager.GetPluginForId(pluginId) is null)
{
    App.API.ShowMsgError($"Plugin '{pluginId}' is not loaded.");
    return;
}

Type guard

static bool IsPluginLoaded(string id) => PluginManager.GetPluginForId(id) is not null;

Try / catch

try { new PluginSettingsWindow(pluginId).Show(); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Unable to find plugin")) { App.API.ShowMsgError(ex.Message); }

Prevention

When it happens

Trigger: Opening the settings window for a pluginId that was unloaded, disabled and garbage-collected, never installed, mistyped, or read from a stale bookmark/shortcut. Also occurs when the settings list is rendered against a plugins.json that has been edited externally and the ID no longer matches.

Common situations: A plugin was uninstalled but its settings row remains in the UI; the user is on a portable build whose Plugins folder differs from the installed build; the plugin ID changed between versions (renamed assembly) and the saved link points at the old ID; concurrent plugin load failed silently so GetPluginForId returns null.

Related errors


AI-assisted analysis of Flow-Launcher/Flow.Launcher@7fc63b07bb (2026-08-13). Data as JSON: /api/errors/3529ba6f5a2e591d. Report an issue: GitHub.