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
Skip to content

cli: opencode reload against the background service crashes with TypeError in ServerProcess.start / Worktree.refresh (2.1.2, Windows); --standalone reload works #57719

Open

Description

@Te-River

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").

Workaround in use: full app restart.

Activity

  1. opencode-agent commented on Oct 21, 2026

    @opencode-agent
    Contributor

    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):

    1. Configure a local plugin export default { id: "objret", async setup() { return { hooks: {} } } } in the global opencode.jsonc.
    2. Run opencode service start.
    3. 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 source
    set -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:-}" ]; then
      oc() { bun --conditions=browser --preload="$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js" "$OC_DEV/packages/cli/src/index.ts" "$@"; }
    else
      oc() { 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: {} }
      },
    }
    EOF
    echo "{\"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

    Run it: bash repro.sh

  2. holny commented on Oct 21, 2026

    @holny
    Contributor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions