cli: `opencode reload` against the background service crashes with TypeError in ServerProcess.start / Worktree.refresh (2.1.2, Windows); --standalone reload works · Issue #57719 · anomalyco/opencode · GitHub
You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
cli: opencode reload against the background service crashes with TypeError in ServerProcess.start / Worktree.refresh (2.1.2, Windows); --standalone reload works #57719
Environment: OpenCode Desktop 2.1.2 (channel=latest), opencode-cli v2.1.2, Windows (win32). Background service healthy: opencode service status -> http://127.0.0.1:45278.
Symptom: opencode reload against the running background service always fails:
ServiceUnavailableError: TypeError: o is not a function. (In 'o()', 'o' is an instance of Object)
at <anonymous> (B:/~BUN/root/chunk-rn0adsfw.js:2:10204)
at <anonymous> (B:/~BUN/root/chunk-rex5bcj8.js:18:6288)
at Effect.fn (B:/~BUN/root/chunk-24qqfd0y.js:2:21592)
at Effect.fn (definition) (B:/~BUN/root/chunk-1gxpxsz5.js:440:15563)
at ServerProcess.start (B:/~BUN/root/chunk-5xxcejxr.js:2:7681)
at ServerProcess.start (definition) (B:/~BUN/root/chunk-1gxpxsz5.js:444:25665)
Run from a different cwd, the same trace shows at Worktree.refresh (B:/~BUN/root/chunk-1gxpxsz5.js:444:20743). The desktop UI's plugin update action surfaces the same TypeError as a toast.
Repro matrix (same machine):
case
result
opencode reload (real global config, any cwd)
TypeError, exit 1
OPENCODE_CONFIG_DIR -> dir with {} opencode.jsonc, real HOME (attaches to running service)
same TypeError, exit 1
fully sandboxed env (HOME/USERPROFILE/TMP/TEMP/OPENCODE_CONFIG_DIR all redirected, empty config, NO plugins) + opencode reload --standalone
Configuration reloaded, exit 0
-> the crash is on the background-service attach path (ServerProcess.start -> Worktree.refresh), independent of config content and plugins (reproduces with zero plugins; plugin code never runs in the CLI process anyway). Likely related to the 2.1.0 contract renames (#57260) — a call site treating an object as a function.
Also: opencode plugin update <pkg> reports Error: Plugin is not configured: <pkg> for a package that IS configured in the global opencode.jsonc (both with and without the @latest suffix) — possibly the same service-attach path failing differently.
Expected: opencode reload reloads the running service's configuration (as its own help promises: "Reload configuration").
Thanks for the detailed report. I reproduced this on 2.1.2, and it still happens on the latest dev (3a1f9a9).
What triggers it: the error happens when a plugin loaded by the background service has a setup() that returns something other than a cleanup function, for example an object. On reload the service tears down the old plugin generation and calls that return value as if it were a cleanup function. The resulting TypeError aborts the whole reload. ServerProcess.start and Worktree.refresh in the trace are only enclosing span labels, not where it breaks.
Why it showed up even with no plugins: your "zero plugins" case doesn't actually remove them. OPENCODE_CONFIG_DIR only changes the config read by the CLI process. The running service still loads your real global config and its plugins. --standalone works because it starts a fresh server with your sandboxed, empty config.
Steps (script below):
Configure a local plugin export default { id: "objret", async setup() { return { hooks: {} } } } in the global opencode.jsonc.
Run opencode service start.
Run opencode reload (once or twice). It fails with ServiceUnavailableError: TypeError: o is not a function. (In 'o()', 'o' is an instance of Object). On dev the message reads cleanup is not a function.
Once the plugin is removed from the config, the next reload works again. So, as a workaround, check your configured plugins for one whose setup returns an object, and update or remove it.
Technically that plugin is breaking the contract (setup should return a cleanup function or nothing). Even so, one plugin shouldn't be able to break reload for everything. This is closely related to #57089 (a throwing plugin cleanup aborts the reload), which has an open PR, #57096.
I haven't checked the separate opencode plugin update "Plugin is not configured" error here.
Reproduction script (repro.sh)
#!/usr/bin/env bash# Reproduces anomalyco/opencode#57719: `opencode reload` against the background# service fails with "TypeError: o is not a function" when a loaded plugin's# setup() returns a non-function value (an object) instead of a cleanup function.## Usage: ./repro.sh # uses `opencode` from PATH (e.g. @opencode/cli@2.1.2)# OC_DEV=/path/to/checkout ./repro.sh # runs the CLI from sourceset -u
export HOME=$(mktemp -d /tmp/opencode/oc57719-XXXX)
PROJ="$HOME/proj"; mkdir -p "$PROJ""$HOME/.config/opencode""$HOME/objret"cd"$PROJ"&& git init -q
if [ -n"${OC_DEV:-}" ];thenoc() { bun --conditions=browser --preload="$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js""$OC_DEV/packages/cli/src/index.ts""$@"; }
elseoc() { opencode "$@"; }
fi# A plugin whose setup() resolves to an object rather than a cleanup function.
cat >"$HOME/objret/index.ts"<<'EOF'export default { id: "objret", async setup() { return { hooks: {} } },}EOFecho"{\"plugins\":[\"$HOME/objret\"]}">"$HOME/.config/opencode/opencode.jsonc"
oc --version
if [ -n"${OC_DEV:-}" ];then# From source, run the service in the foreground process (spawned children lack the preload).
oc serve --service >"$HOME/serve.log"2>&1&
SERVE_PID=$!; sleep 8
else
oc service start; sleep 5
fi
oc plugin list
echo"--- reload #1 (activates the plugin if not yet active) ---"
oc reload;echo"exit=$?"
oc plugin list
echo"--- reload #2 (tears down the active plugin generation) ---"
oc reload;echo"exit=$?"# Change the config so the old plugin generation is torn down on reload.echo'{"plugins":[]}'>"$HOME/.config/opencode/opencode.jsonc"echo"--- reload after config change ---"
oc reload;echo"exit=$?"
grep -h "not a function""$HOME"/.local/share/opencode/log/*.log | tail -3
oc service stop >/dev/null 2>&1||true
[ -n"${SERVE_PID:-}" ] &&kill"$SERVE_PID"2>/dev/null ||true
I'd like to take this. The bot's diagnosis matches the plugin teardown path: on reload the previous plugin generation is disposed and the value setup() returned is called as a cleanup function without checking that it actually is one, so a plugin whose setup() returns an object takes the whole reload down with it. I'll guard the teardown to only invoke function return values, add a test covering a plugin whose setup() returns a non-function, and base against origin/dev.
Activity
opencode-agent commentedon Oct 21, 2026
Thanks for the detailed report. I reproduced this on 2.1.2, and it still happens on the latest
dev(3a1f9a9).What triggers it: the error happens when a plugin loaded by the background service has a
setup()that returns something other than a cleanup function, for example an object. On reload the service tears down the old plugin generation and calls that return value as if it were a cleanup function. The resultingTypeErroraborts the whole reload.ServerProcess.startandWorktree.refreshin the trace are only enclosing span labels, not where it breaks.Why it showed up even with no plugins: your "zero plugins" case doesn't actually remove them.
OPENCODE_CONFIG_DIRonly changes the config read by the CLI process. The running service still loads your real global config and its plugins.--standaloneworks because it starts a fresh server with your sandboxed, empty config.Steps (script below):
export default { id: "objret", async setup() { return { hooks: {} } } }in the globalopencode.jsonc.opencode service start.opencode reload(once or twice). It fails withServiceUnavailableError: TypeError: o is not a function. (In 'o()', 'o' is an instance of Object). Ondevthe message readscleanup is not a function.Once the plugin is removed from the config, the next reload works again. So, as a workaround, check your configured plugins for one whose
setupreturns an object, and update or remove it.Technically that plugin is breaking the contract (
setupshould return a cleanup function or nothing). Even so, one plugin shouldn't be able to breakreloadfor everything. This is closely related to #57089 (a throwing plugin cleanup aborts the reload), which has an open PR, #57096.I haven't checked the separate
opencode plugin update"Plugin is not configured" error here.Reproduction script (repro.sh)
Run it:
bash repro.shholny commentedon Oct 21, 2026
I'd like to take this. The bot's diagnosis matches the plugin teardown path: on reload the previous plugin generation is disposed and the value
setup()returned is called as a cleanup function without checking that it actually is one, so a plugin whosesetup()returns an object takes the whole reload down with it. I'll guard the teardown to only invoke function return values, add a test covering a plugin whosesetup()returns a non-function, and base againstorigin/dev.