Repository navigation
Description
Summary
A local plugin file that is a symlink never picks up edits while the server is running. The reload path correctly detects the change (the digest read follows the symlink) and re-imports the entrypoint, but the import returns the previously cached module — Bun caches it under the resolved realpath, while opencode invalidates its cache under the symlink path. The running server keeps the pre-edit module until the process restarts.
Environment
- opencode version: 2.0.14
- OS: macOS 15 (Darwin 25.5.0, darwin arm64)
- Terminal: iTerm2 (TERM=xterm-256color)
- Shell: zsh
- Install/channel: latest (
~/.opencode/bin/opencode) - Active plugins: two local file plugins auto-discovered from
$XDG_CONFIG_HOME/opencode/plugins/— one regular file (hot-reloads fine), one symlink (bug)
Reproduction
- In a scratch
$XDG_CONFIG_HOME/opencode/plugins/create two identical plugin files:regular.ts— regular file:export default { id: "regular-probe", setup() {} }linked.ts— a symlink to a file elsewhere (dotfiles checkout): same content,export default { id: "linked-probe", setup() {} }
- Start
opencode serve, create a session,GET /api/plugin→ both entriesactive. - Edit both files (for the symlink, edit its target) to remove the default export:
export const notDefault = 1 GET /api/pluginagain.
Expected Behavior
Both entries re-evaluate the new content and both flip to failed with Plugin must export a default definition with an id and an effect or setup function.
Actual Behavior
regular.ts: reloads — new content evaluated, state becomesfailed(correct).linked.ts:loading pluginis logged again (watcher + digest change both work — the digest is read through the symlink), but the import resolves to the cached pre-edit module; the entry staysactivewith the old id and old behavior. Only a server restart picks up the change.
Both files were edited in the same second, in the same server process:
02:17:24.249 msg="loading plugin" id=.../plugins/regular.ts
02:17:24.249 message="failed to load plugin" target=.../regular.ts ← new content evaluated
02:17:24.558 msg="loading plugin" id=.../plugins/linked.ts
(no failure, no change — import returned the stale module)
Root cause (traced in v2.0.14)
packages/plugin/src/source.bun.ts:12—const root = fileURLToPath(entrypoint)is the symlink path.packages/plugin/src/source.bun.ts:21—delete require.cache[file]invalidates by that symlink path only.- The subsequent load (
packages/plugin/src/source.ts:30-39→packages/plugin/src/host.ts:46-48→packages/util/src/runtime/import.bun.ts:3, a plain dynamicimport()) resolves the module by its realpath, so the cache deletion misses and the stale module is returned.
Notably the node runtime path already solves this by appending a cache-busting __opencode_reload query param to every specifier (packages/plugin/src/source.node.ts:12-16); the bun path has no equivalent and relies solely on the require.cache deletion that misses symlinks.
Real-world impact: a plugin fixed from a v1-style shape to export default { id, setup } via a dotfiles-managed symlink kept the running service logging the schema failure on every /api/plugin poll for two days, while every fresh process loaded the fixed file fine — very confusing to debug since the on-disk file was already correct.
Workarounds
opencode service restart(or restart the serve process) after editing a symlinked plugin.- Keep regular files in
plugins/instead of symlinks.
Possible fix directions: resolve to realpath (fs.realpathSync) before the require.cache deletion for both root and tracked files, or add the bun equivalent of the __opencode_reload cache-busting suffix used on the node path.
Activity
sblattj commentedon Sep 27, 2026
Possibly the same underlying pattern (symlink path taken literally instead of resolving to realpath) in a sibling component: #39738 (symlinked global
opencode.jsonsilently ignored). Cross-linking in case a shared canonicalization fix is preferable to per-component patches.github-actions commentedon Sep 27, 2026
This issue does not fully meet our contributing guidelines.
What needs to be fixed:
Please edit this issue to address the above within 2 hours, or it will be automatically closed.
This issue might be a duplicate of existing issues. Please check:
If you believe this was flagged incorrectly, please let a maintainer know.
github-actions commentedon Sep 27, 2026
This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.
Feel free to open a new issue that follows our issue templates.