Skip to content

[BUG] Desktop v1.18.1: file:// plugin persists in UI after removal from all config sources #37099

Closed as not planned

Description

@mocun33

Bug Description

After removing a \ ile:///D:/plugins/bug-compaction.ts\ plugin from all known config sources and restarting OpenCode Desktop, the plugin still appears in the Desktop UI plugin list and generates \Failed to load plugin\ errors on every startup.

Environment

  • OpenCode Desktop: v1.18.1 (Electron) — Windows 11
  • opencode.jsonc: %USERPROFILE%\.config\opencode\opencode.jsonc\

Steps to Reproduce

  1. Add a local file plugin (via plugins/ auto-discovery or \ ile://\ specifier)
  2. Remove it from \opencode.jsonc:plugin\ array
  3. Delete the physical file
  4. Remove from \ ui.json, \plugins/\ directories, .opencode/\ directories (if present)
  5. Restart OpenCode Desktop
  6. Observe: plugin still appears in UI plugin list, \Failed to load plugin\ errors at startup

What Was Checked

All verified clean (no stale reference):

Location Status
\opencode.jsonc\ \plugin\ array ✅ Clean
\ ui.json\ ✅ Clean
~/.config/opencode/plugins/\ directory ✅ No file
.opencode/\ directory chain ✅ None exist
\plugin-meta.json\ ✅ Only oh-my-openagent
\opencode.global.dat\ (all 11 keys) ✅ No plugin field
18x \opencode.workspace.*.dat\ files ✅ No match
\opencode.db\ SQLite (19 tables) ✅ No plugin table
Physical file ✅ Deleted
Restart ✅ Bug persists

Root Cause (source code analysis)

In \packages/opencode/src/config/config.ts:

  • \plugin_origins\ is typed as derived state, not a persisted config field
  • \writable(info)\ strips \plugin_origins\ before serialization
  • \mergePluginOrigins()\ is append-only: calls \deduplicatePluginOrigins()\ but never removes entries absent from current config sources
  • \loadInstanceState()\ rebuilds \plugin_origins\ each startup by iterating config sources

The Desktop v2 migration (v1.18.0) may retain a stale \plugin_origins\ snapshot that re-enters the merge chain.

Related Issues

Activity

  1. mocun33 commented on Jul 15, 2026

    @mocun33
    Author

    Evidence: Startup Error Persists

    From %APPDATA%\ai.opencode.desktop\opencode.global.dat\ (
    otification\ key) — the error is recorded every startup:

    \
    Failed to load plugin file:///D:/plugins/bug-compaction.ts:
    Cannot find module 'D:\\plugins\\bug-compaction.ts' imported from
    C:\\Users\\0\\AppData\\Local\\Programs\\@opencode-aidesktop\\resources\\app.asar\\out\\main\\chunks\\node-BxVSsTOf.js
    \\

    This error appears across multiple startup sessions (timestamps span different runs), confirming the plugin registry survives restart.

  2. mocun33 commented on Jul 15, 2026

    @mocun33
    Author

    Evidence: Clean Config & plugin-meta

    opencode.jsonc plugin array — 5 entries, none is bug-compaction:

    plugin: [
      oh-my-opencode-slim,
      superpowers@git+https://github.com/obra/superpowers.git,
      opencode-triage,
      opencode-mcp-triage,
      ./plugins/compaction-hook.ts
    ]
    

    plugin-meta.json (%APPDATA%\ai.opencode.desktop\opencode\plugin-meta.json) — only oh-my-openagent, no stale entry:

    {
      oh-my-openagent:tui: {
        id: oh-my-openagent:tui,
        source: npm,
        spec: oh-my-openagent,
        target: C:\\Users\\0\\.cache\\opencode\\packages\\oh-my-openagent@latest\\node_modules\\oh-my-openagent,
        requested: latest,
        version: 4.11.1
      }
    }

    electron-store (.dat files) — All 19 .dat files searched, zero match for bug-compaction or D:/plugins.

  3. mocun33 commented on Jul 15, 2026

    @mocun33
    Author

    Source Code Analysis

    From packages/opencode/src/config/config.ts (Desktop app.asar decompiled, v1.18.1):

    plugin_origins is derived state

    type Info = ConfigV1.Info & {
      // plugin_origins is derived state, not a persisted config field.
      plugin_origins?: ConfigPlugin.Origin[]
    }
    
    function writable(info: Info) {
      const { plugin_origins: _plugin_origins, ...next } = info
      return next
    }

    mergePluginOrigins is append-only — never prunes

    const mergePluginOrigins = Effect.fnUntraced(function* (
      source: string,
      list: ConfigPlugin.Spec[] | undefined,
      kind?: ConfigPlugin.Scope,
    ) {
      if (!list?.length) return
      const hit = kind ?? (yield* pluginScopeForSource(source))
      // Merge newly seen plugin origins with previously collected ones, then dedupe.
      // NOTE: This only deduplicates — it NEVER removes entries absent from current config sources.
      const plugins = ConfigPlugin.deduplicatePluginOrigins([
        ...(result.plugin_origins ?? []),   // <-- carries forward ALL previous entries
        ...list.map((spec) => ({ spec, source, scope: hit })),
      ])
      result.plugin = plugins.map((item) => item.spec)
      result.plugin_origins = plugins
    })

    The function mergePluginOrigins only adds and deduplicates — it never starts from a clean slate. The spread ...(result.plugin_origins ?? []) carries forward every previously-merged entry, even if the source config file that declared it no longer contains it.

    Auto-discovery also cannot explain it

    The ConfigPlugin.load(dir) function scans {plugin,plugins}/*.{ts,js} within each config directory. The stale plugin is at D:\plugins\bug-compaction.ts — not in any standard config directory. Auto-discovery cannot produce this path.

    Hypothesis

    A stale plugin_origins entry from an earlier session is being carried forward by the append-only merge. Since plugin_origins is stripped by writable() before serialization, it should be rebuilt each startup. The fact that it persists suggests either:

    1. A Desktop v2 migration (v1.18.0) cached plugin state outside the standard config path
    2. The sidecar server process retains plugin state in an unaccounted-for storage layer (possibly in opencode.db or an indexed store not accessible via filesystem search)
    3. mergePluginOrigins is being called with stale data from a source not captured by the config file audit (well-known URL, OPENCODE_CONFIG_CONTENT, etc.)
  4. mocun33 commented on Jul 17, 2026

    @mocun33
    Author

    v1.18.3 update: ghost no longer generates NEW errors on restart

    Upgraded to Desktop v1.18.3. After restart:

    • Checked opencode.global.dat notification history -- no new entries for bug-compaction.ts since restart
    • All 122 old bug-compaction references in store are from pre-v1.18.3 runs
    • Old entries persist as viewed:false in notification history but Desktop no longer actively tries to load the ghost plugin

    This might mean v1.18.3 partially addresses the issue (stops the loading attempt), but old notification entries are never cleaned up.

    Partial fix or just different behaviour in my specific case.

  5. github-actions commented on Sep 17, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions