anomalyco / anomalyco/opencode

plugins: config-entry options silently ignored when the spec duplicates an auto-discovered local plugin

Open
#48,706 0 comments 0 reactions 1 assignee View on GitHub

@Hona is already working on this.

Since Sep 12, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
Avg merge
7h 2m
Merged PRs (30d)
384

Description

Summary

A plugins config entry that targets a local plugin package also auto-discovered from the global plugins directory registers a second instance with the same plugin id when the package spec uses forward slashes on Windows. The second instance fails with Duplicate plugin ID: <id>, and its options are silently ignored — the plugin runs with defaults and nothing tells the user the options were dropped.

Environment

  • opencode version: 2.0.2
  • OS: Windows 11 (10.0.26220.9343, win32 x64)
  • Terminal: Unavailable: reproduced against the background service via opencode api (no TUI attached)
  • Shell: cmd.exe
  • Install/channel: latest (2.0.2)
  • Active plugins: 4 local entries auto-discovered from ~/.config/opencode/plugins/; no plugins key in the global config at rest (the test entry was removed after the repro)

Reproduction

  1. Create a local plugin package at ~/.config/opencode/plugins/example/ whose index.ts default-exports Plugin.define({ id: "example", setup(ctx) { /* register a tool that returns JSON.stringify(ctx.options) */ } }).

  2. Add a global config entry using an absolute path with forward slashes:

    {
      "plugins": [
        { "package": "C:/Users/<user>/.config/opencode/plugins/example", "options": { "foo": "bar" } }
      ]
    }
    
  3. Let the config reload, then inspect the plugin list — opencode api get /api/plugin:

    • two entries with id: "example": the first status: "active", the second status: "failed" with "error": "Duplicate plugin ID: example";
    • the running plugin reports ctx.options === {} — the configured options never arrive.
  4. Change the spec to the same directory with native separators (C:\Users\<user>\.config\opencode\plugins\example) or to a config-relative path ("./plugins/example"): the list shows exactly one active entry, and ctx.options is { "foo": "bar" }.

Expected Behavior

At least one of:

  • path specs are canonicalized before duplicate detection, so the config entry dedupes against the auto-discovered package and its options are applied; or
  • the entry's options are merged into the already-loaded instance; or
  • at minimum, the dropped options are surfaced as a warning.

Actual Behavior

The config entry produces a rejected duplicate (Duplicate plugin ID: example) while the option-less auto-discovered instance keeps running. Options are silently dropped; the only trace is a failed entry in /api/plugin, and the server log shows only msg="loading plugin" lines with no warning.

Additional Context

  • Verified repeatedly in both directions: forward-slash absolute path → duplicate + options ignored; backslash absolute or config-relative → single instance + options applied.
  • Related: #7427 fixed duplicate loading in V1 via canonical-name dedupe; the V2 behavior above shows spec canonicalization still misses Windows separator styles.
  • Impact: forward-slash absolute paths are a common cross-platform spelling, so users can silently lose plugin options — the plugin still works, just without its configuration, which is hard to notice.
  • Workaround: write absolute specs with native separators, or use a path relative to the config file when the package sits beside it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.