Repository navigation
Open
Description
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, channellocal) - 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
- Configure a local plugin by absolute file path:
where
{ "plugins": [ "/Users/me/opencode-plugin/plugin.js" ] }plugin.jshas a default export (export default { id, setup }). This worked before 2026-08-30. - Start
opencode serve(or any server that loads plugins). - The plugin is not loaded. The server log contains:
No error is surfaced to the user; the plugin simply disappears (its models, tools, and skills are gone).
level=WARN message="configured plugin path must be a directory" target=/Users/me/opencode-plugin/plugin.js
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
WARNlog 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.mdxexamples include"../shared/plugin.ts","/absolute/path/plugin.ts", and"file:///home/me/plugins/local".config.mdxsays "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.jsre-exporting the plugin file. Package (npm/Git) plugin entries are unaffected because they resolve throughpackage.jsonexports/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.
Reacted by igor
Metadata
Metadata
Assignees
Labels
No labels
Activity
RaviTharuma commentedon Sep 12, 2026
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
WARNthis issue describes for file paths. Verified on 1.18.30, same machine, same config shape:/tmp/pluginrepro/nope.js(this issue)WARNline./plugins/vendor/does-not-exist-anywhere-0.0.1So 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-lookingProviderModelNotFoundError.Filed separately as #48577 since it's a distinct branch, but if you're already touching
source.tsfor this, both cases likely want the same treatment.wppqqppq commentedon Oct 13, 2026
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 (nomsg="loading plugin"line, no other diagnostic).Every file form fails on 2.0.27:
pluginsentry"plugin": ["/Users/me/rtk_dev/rtk.ts"](legacy key)WARN configured plugin path must be a directory"plugins": ["/Users/me/rtk_dev/rtk.ts"]"plugins": ["./rtk.ts"]"plugins": ["file:///Users/me/rtk_dev/rtk.ts"]"plugins": ["/Users/me/rtk_dev/plugin.js"](.js, not.ts)So it is neither
.ts-specific nor limited to the new key.What works on 2.0.27:
.opencode/plugins/<any-name>.tsloads and runs — including a symlink placed in that directory. Verified end-to-end: the loaded plugin'sexecute.beforehook rewrote a shell command (ls -la package.jsonexecuted asrtk ls -la package.json, permessage="spawning process" command=/bin/zsh args=["-c","rtk ls -la package.json"]).index.ts— see plugins: local plugin directory ignores package.json main and is silently skipped without a root entrypoint #52300 (package.jsonmainis ignored: a directory holdingpackage.jsonwithmain→plugin.ts, plusplugin.ts, and noindex.ts, loads nothing).The docs still list
../shared/plugin.tsand/absolute/path/plugin.tsunderplugins(https://opencode.ai/v2/docs/plugins/), so the doc/behaviour mismatch reported here is still live at the latest release.opencodejr commentedon Oct 28, 2026
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:
WARNlog 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.jsat its root or apackage.jsonwithnameandexports, as you found.Related issues:
pluginsare silently never loaded; plugin import failures produce no log #58647: absolute-path directory entries that are silently never loaded