Skip to content

plugin: configured local plugin file paths silently dropped since #46105 #46551

Open

Description

@luisherranz

Summary

Since #46105 (2026-08-30), configured plugin entries that resolve to an absolute file path are silently dropped with only a level=WARN log line ("configured plugin path must be a directory"), while the plugins docs still document file paths as valid config entries. Upgrading silently disables local plugins.

Environment

  • opencode version: v2 branch (source build at 71779ae5de, channel local)
  • OS: Darwin 25.6.0 (darwin arm64)
  • Terminal: Unavailable (headless server context)
  • Shell: /bin/zsh
  • Install/channel: source build (channel local), v2 line
  • Active plugins: two local plugins configured by absolute path (see Reproduction)

Reproduction

  1. Configure a local plugin by absolute file path:
    {
      "plugins": [
        "/Users/me/opencode-plugin/plugin.js"
      ]
    }
    where plugin.js has a default export (export default { id, setup }). This worked before 2026-08-30.
  2. Start opencode serve (or any server that loads plugins).
  3. The plugin is not loaded. The server log contains:
    level=WARN message="configured plugin path must be a directory" target=/Users/me/opencode-plugin/plugin.js
    
    No error is surfaced to the user; the plugin simply disappears (its models, tools, and skills are gone).

Expected Behavior

Either:

  • Configured file paths keep working (resolve the file directly as the plugin entrypoint, as before feat(plugin): add typed rpc and custom events #46105), or
  • The docs are updated to remove file-path examples, and the drop is surfaced loudly (config error / visible warning) instead of a WARN log line — silently losing plugins on upgrade is the worst outcome.

Actual Behavior

packages/core/src/config/plugin/source.ts (since 6a2c3e91c7, #46105) drops any configured target that fs.isFile with the warning "configured plugin path must be a directory" and returns Option.none, so the plugin is never loaded. This also affects relative file paths (../shared/plugin.ts) and file:// entries pointing at files, which resolve to absolute paths before the check.

Additional Context

  • The docs still show file paths as valid: packages/www/src/docs/content/plugins.mdx examples include "../shared/plugin.ts", "/absolute/path/plugin.ts", and "file:///home/me/plugins/local". config.mdx says "Load plugins from packages or local plugin directories" and only shows directory examples.
  • Directory targets only work when the directory contains an index.ts/index.js (PluginSourceDirectory.entrypoint); a directory without one is also dropped ("configured plugin directory has no index entrypoint").
  • Workaround: point the config at the plugin directory and add an index.js re-exporting the plugin file. Package (npm/Git) plugin entries are unaffected because they resolve through package.json exports/main.
  • This bit in production on a self-hosted instance: both configured plugins (a subscription auth plugin and a pipeline plugin) silently vanished after rebasing onto the v2 branch that includes feat(plugin): add typed rpc and custom events #46105; models provided by the subscription plugin disappeared from the catalog.

Activity

  1. RaviTharuma commented on Sep 12, 2026

    @RaviTharuma

    Adding a data point on a neighbouring branch of the same resolver, in case it's useful to fix together.

    A configured plugin path that points to a non-existent directory produces no diagnostic at all — not even the WARN this issue describes for file paths. Verified on 1.18.30, same machine, same config shape:

    Config entry Diagnostic
    /tmp/pluginrepro/nope.js (this issue) WARN line
    ./plugins/vendor/does-not-exist-anywhere-0.0.1 none
    $ opencode run --print-logs 'hi' 2>&1 | grep -c 'does-not-exist-anywhere'
    0
    

    So the file branch is at least observable via --print-logs, while the missing-directory branch is completely silent. Your point that "silently losing plugins on upgrade is the worst outcome" applies with full force here: in my case it silently disabled a provider and every agent bound to it fell through to a fallback tier for hours before surfacing an unrelated-looking ProviderModelNotFoundError.

    Filed separately as #48577 since it's a distinct branch, but if you're already touching source.ts for this, both cases likely want the same treatment.

  2. wppqqppq commented on Oct 13, 2026

    @wppqqppq

    Confirming this still reproduces on the latest release, not only on a source build — opencode 2.0.27 (npm view @opencode/cli version → 2.0.27). The entry is dropped with exactly this warning and the plugin is skipped silently (no msg="loading plugin" line, no other diagnostic).

    Every file form fails on 2.0.27:

    plugins entry result
    "plugin": ["/Users/me/rtk_dev/rtk.ts"] (legacy key) WARN configured plugin path must be a directory
    "plugins": ["/Users/me/rtk_dev/rtk.ts"] same
    "plugins": ["./rtk.ts"] same
    "plugins": ["file:///Users/me/rtk_dev/rtk.ts"] same
    "plugins": ["/Users/me/rtk_dev/plugin.js"] (.js, not .ts) same

    So it is neither .ts-specific nor limited to the new key.

    What works on 2.0.27:

    • Discovery: .opencode/plugins/<any-name>.ts loads and runs — including a symlink placed in that directory. Verified end-to-end: the loaded plugin's execute.before hook rewrote a shell command (ls -la package.json executed as rtk ls -la package.json, per message="spawning process" command=/bin/zsh args=["-c","rtk ls -la package.json"]).
    • A config entry pointing at a directory, but only with a root index.ts — see plugins: local plugin directory ignores package.json main and is silently skipped without a root entrypoint #52300 (package.json main is ignored: a directory holding package.json with main → plugin.ts, plus plugin.ts, and no index.ts, loads nothing).

    The docs still list ../shared/plugin.ts and /absolute/path/plugin.ts under plugins (https://opencode.ai/v2/docs/plugins/), so the doc/behaviour mismatch reported here is still live at the latest release.

  3. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed write-up and the workaround. You were right that the docs and the loader disagreed when you filed this. Since then the plugins docs have been updated (#58280) to match the code. They now show only local plugin directories and say: "Configured local paths must be directories. A path to a single file is skipped." So dropping file entries is the intended v2 behaviour, and I'm treating this as a feature request with two parts:

    • accept a single file as a configured plugin entrypoint again, and/or
    • when a configured plugin is skipped, report it clearly (for example as a failed plugin or a visible warning) instead of only a WARN log line, so plugins don't silently disappear after an upgrade.

    Maintainers will consider it. Until then, point the entry at the plugin directory, with an index.js/server.js at its root or a package.json with name and exports, as you found.

    Related issues:

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