AI & DevelopmentOpen SourceDeveloper Tools

OpenCode V2: Your Plugins Are Broken — Here Is What Changed

OpenCode V2 plugin API migration — V1 vs V2 code comparison
OpenCode V2 ships a breaking plugin API change. V1 plugins silently fail on V2.

OpenCode V2 shipped September 11 and the plugin API is a hard break. V1 plugins do not load on V2 — they throw an error and stop executing with zero warning. If you maintain an OpenCode plugin, or rely on a community plugin that has not migrated yet, your tool is silently dead. Here is exactly what changed and how to fix it.

What OpenCode Is (Quick Version)

OpenCode is the leading open-source AI coding agent for the terminal, built by SST. It supports 75+ LLM providers, runs local models via Ollama and llama.cpp, and has become the primary Cursor alternative since SpaceX acquired Cursor in September. The plugin API is how developers extend it — add custom tools, route model requests, inject context. V2 rewrote that API entirely.

The Three Breaking Changes

OpenCode V2 has three intentional breaking changes:

  • Plugin API — completely new contract; V1 plugins do not run on V2
  • Server API and client contracts — internal communication layer replaced
  • Terminal config — layered tui.json files replaced by a single cli.json (auto-migrated on first launch)

The config migration is automatic. The server API change matters only if you wrote server-side extensions. The plugin API is where the real pain is.

What the Plugin API Actually Changed

The V1 plugin was a bare async function. V2 requires a named definition object. If your plugin still exports a bare function, V2 throws this error and moves on:

Plugin must export a default definition with an id and an effect or setup function.

Here are the five specific API changes:

1. Export Shape

V1 exported an async function. V2 requires Plugin.define({ id, setup(ctx) }). No id, no load.

2. Event Subscription

Replace ctx.subscribe() with ctx.event.subscribe(). The event data envelope also changed — properties are now nested under data, not at the top level.

3. Session Info

Replace ctx.session.context() with ctx.session.get({ sessionID }).

4. Permissions

Replace direct _client.get() calls with ctx.permission.list({ sessionID }).

5. Model Request Hooks

The single chat.params hook is gone. V2 splits model requests into four distinct kinds: context, compaction, generate, and title. Any plugin that touched chat.params needs a full rewrite of that section.

The Code: V1 vs V2

Here is a minimal before and after:

// V1 plugin — BREAKS silently on OpenCode 2.x
export default async function(ctx) {
  ctx.subscribe("session.created", ({ sessionID }) => {
    console.log("Session started:", sessionID)
  })
}

// V2 plugin — required for OpenCode 2.x
import { Plugin } from "@opencode/core"
export default Plugin.define({
  id: "my-plugin",
  setup(ctx) {
    ctx.event.subscribe("session.created", ({ data }) => {
      console.log("Session started:", data.sessionID)
    })
    return () => {} // return cleanup function
  }
})

Plugin Authors: The Dual Compatibility Shim

If you maintain a public plugin and need to support both V1 and V2 users, you do not have to ship two packages. The official pattern is a dual default export that both versions can consume:

// Works on both V1 (>=1.18.29) and OpenCode 2.x
import { Plugin } from "@opencode/core"

const v2 = Plugin.define({
  id: "my-plugin",
  setup(ctx) {
    // V2 logic here
  }
})

async function server(ctx) {
  // V1 logic here
}

export default { ...v2, server }

The trick is in the directories: V2 auto-discovers plugins from .opencode/plugins/ (plural). V1 reads from .opencode/plugin (singular). Drop the same package in each directory and both versions load their respective entry point — no version sniffing, no runtime checks. The official migration guide covers the full dual-compatible directory layout.

The LSP Gotcha Nobody Is Talking About

V2 accepts LSP configuration — it just ignores it completely. V2 does not run language servers, does not expose LSP tools, and does not produce LSP diagnostics. If you have a CI pipeline that relies on LSP-driven feedback loops or a plugin that reads diagnostic data, it will break quietly. Replace those workflows with direct compiler or linter commands: npm run typecheck, cargo check, mypy .. Plan Mode and Build Mode are unaffected.

What Did Not Break

Server config, agent definitions, command definitions, skills, and all other files in .opencode/ carry over intact. The upgrade is painless if you have no plugins.

New Models on the Zen List

Separate from the plugin change, OpenCode v1.18.32 added two models to the Zen curated list: Grok 4.7 at $2 input / $6 output per million tokens, and DeepSeek V4.1 Flash. Both are available in V1 and V2. Grok 4.7 is notably cheap compared to frontier alternatives — a useful option when you are already thinking about model routing and cost reduction.

Who Needs to Act and When

Plugin authors: Act now. Check if your plugin uses any of the five changed APIs. Either migrate to V2 or ship the dual shim. Users on V2 are already seeing silent failures.

Plugin users: If a plugin stopped working after upgrading to V2, check its GitHub issues for a “V2 Plugin API compatibility” ticket. Pin to V1 with npm install -g @opencode/cli@1 until the plugin migrates.

Everyone else: The upgrade from V1 to V2 is smooth. Config auto-migrates, Plan Mode and Build Mode work as before. Remove V1 before installing V2 — both binaries use the opencode command. Check the full changelog for what else landed.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *