stablyai/orca · error · Error

cannot remove protected or non-installed plugin ${parsed.plu

Error message

cannot remove protected or non-installed plugin ${parsed.pluginKey}

What it means

Thrown at plugins.ts:219-220 when canRemoveInstalledPlugin(pluginService, pluginKey, lock) returns false. That function (plugins.ts:79-88) returns true only if BOTH conditions hold: the plugin is not sourced as 'bundled' in the lockfile, AND the plugin is discovered and not marked isDev. So the error covers three distinct cases: the plugin is bundled/protected, it is a dev plugin, or it is not installed at all.

Source

Thrown at src/main/ipc/plugins.ts:220

            pluginsDir,
            url: parsed.url,
            ref: parsed.ref,
            hostVersion,
            blockedPluginReason
          })
    if (result.ok) {
      await pluginService.refresh()
    }
    return result
  })

  ipcMain.handle('plugins:remove', async (event, args: unknown) => {
    await pluginService.whenReady()
    const parsed = removeArgsSchema.parse(args)
    const pluginsDir = getUserPluginsDir(pluginService.options.userDataPath)
    const lock = await readPluginLockfile(pluginsDir)
    if (!canRemoveInstalledPlugin(pluginService, parsed.pluginKey, lock)) {
      throw new Error(`cannot remove protected or non-installed plugin ${parsed.pluginKey}`)
    }
    await pluginService.deactivatePlugin(parsed.pluginKey)
    await removeInstalledPlugin({
      pluginsDir,
      pluginsDataDir: getPluginsDataDir(pluginService.options.userDataPath),
      pluginKey: parsed.pluginKey
    })
    // Drop the stale consent so a later reinstall re-prompts from scratch.
    const settings = store.getSettings()
    const consents = { ...settings.pluginConsents }
    delete consents[parsed.pluginKey]
    const disabledPlugins = normalizePluginIdList(settings.disabledPlugins).filter(
      (pluginKey) => pluginKey !== parsed.pluginKey
    )
    store.updateSettings(
      { pluginConsents: consents, disabledPlugins },
      { notifyListeners: true, originWebContentsId: event.sender.id }
    )

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Confirm the pluginKey exactly matches a discovered, non-dev, non-bundled plugin (check the plugins:list output).
  2. If it is a dev plugin, remove it by editing the devPluginPaths setting instead of calling plugins:remove.
  3. If it was already removed, no action is needed — refresh the plugin list.
  4. Bundled plugins cannot be removed by design; do not expose a remove affordance for them in the UI.
Defensive patterns

Strategy: type-guard

Validate before calling

// Before calling plugins:remove, check removability the same way the handler does.
const lock = await readPluginLockfile(getUserPluginsDir(userDataPath))
const removable =
  lock?.plugins[pluginKey]?.source.kind !== 'bundled' &&
  pluginService.getDiscovered().some((p) => p.pluginKey === pluginKey && !p.isDev)
if (!removable) { /* hide/disable the remove button */ }

Type guard

function isRemovablePlugin(plugin: { isDev: boolean }, sourceKind?: string): boolean {
  return !plugin.isDev && sourceKind !== 'bundled'
}

Try / catch

try { await window.api.invoke('plugins:remove', { pluginKey }) }
catch (e) { if (e instanceof Error && e.message.startsWith('cannot remove protected or non-installed plugin')) { /* refresh list; it may already be gone */ } else throw e }

Prevention

When it happens

Trigger: Calling the plugins:remove IPC handler with a pluginKey that (a) is shipped/bundled with the app (lock.plugins[key].source.kind === 'bundled'), (b) is loaded from a dev path (plugin.isDev true), or (c) is not in the discovered plugin list (never installed or already removed).

Common situations: UI tries to show a remove button for a built-in plugin; a plugin was uninstalled out-of-band so the discovered list no longer has it; attempting to remove a dev plugin via the UI instead of editing devPluginPaths; typo in the pluginKey.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/4f099bec9a31191a. Report an issue: GitHub.