Repository navigation
Description
Summary
The V2 plugin migration guide lists catalogs among the plugin context domains, but no catalog domain exists in the V2
plugin API (verified against @opencode/plugin@2.0.29 and a running OpenCode 2.0.29). A published plugin that trusted
that list fails to load with TypeError: undefined is not an object (evaluating 'catalog.transform').
Environment
- opencode version: 2.0.29 (
opencode v2.0.29) - OS: Microsoft Windows [Version 10.0.26200.9457] (win32 x64)
- Terminal: TERM_PROGRAM=vscode, TERM=xterm-256color, COLORTERM=truecolor
- Shell: C:\WINDOWS\system32\cmd.exe (PowerShell 5.1.26100.9444 for the interactive session)
- Install/channel: latest
- Active plugins:
cursor-oauth-opencode@latest(package,status=failed),i-have-adhd(local.ts,status=active)
Reproduction
-
Configure the plugin in the global config (
~/.config/opencode/opencode.jsonc):{ "$schema": "https://opencode.ai/config.json", "plugins": ["cursor-oauth-opencode@latest"] } -
Restart OpenCode, or let the config watcher reload it.
-
Read the plugin registry:
opencode api get /api/plugin. -
Search the returned list for the entry whose
source.targetiscursor-oauth-opencode@latest.
Expected Behavior
Either:
- the documented
catalogsdomain exists on the plugin context and the plugin loads, or - the migration guide does not advertise a domain that the plugin API does not provide.
Actual Behavior
The plugin is reported as failed:
{
"id": "cursor-oauth-opencode",
"source": { "type": "package", "target": "cursor-oauth-opencode@latest", "version": "0.22.1" },
"state": {
"status": "failed",
"error": "TypeError: undefined is not an object (evaluating 'catalog.transform')\n at publishCursorCatalog (...\\opencode\\npm\\cursor-oauth-opencode@latest\\<hash>\\node_modules\\cursor-oauth-opencode\\dist\\v2-entry.js:156:11)\n at setup (...\\dist\\v2-entry.js:64:15)"
}
}The plugin's V2 entrypoint calls an undocumented domain:
// dist/v2-entry.js:64
await publishCursorCatalog(ctx.catalog, runtime.getCachedModels());ctx.catalog is undefined, so the plugin's setup() throws and the plugin never activates.
Additional Context
Documentation says the domain exists
/build/plugins/migrate-v1, section "Migrate the context", states:
The context exposes many more domains than the table lists, including catalogs, commands, integrations, MCP servers,
references, skills, storage, VCS, worktrees, and web search. See the complete plugin context.
However /build/plugins#api has no Catalogs section. The documented API sections are: Agent, Providers, Models,
Commands, Integrations, Credentials, MCP, Plugins, References, Generate, Permissions, Sessions, Skills, Storage, Tools,
VCS, Worktrees, Websearch, Events, plus the hook sections (Sessions, Permissions, Shell, Tools).
The published SDK has no catalog domain
npm install @opencode/plugin@2.0.29 produces these context domain modules under dist/effect/:
agent aisdk command event integration mcp model permission plugin
provider reference registration rpc session shell skill storage tool
vcs websearch worktree
There is no catalog module and no catalog export. The only catalog symbol anywhere in the package is
ctx.mcp.resource.catalog(...) (McpResourceCatalogOperation), which is unrelated to publishing a provider/model
catalog.
Impact
- Plugin authors following the migration guide can target a context member that does not exist.
- Affects a real published plugin:
cursor-oauth-opencode@0.22.1(latest on npm, published 2026-08-20) ships a V2
entrypoint and cannot load in OpenCode 2.0.29, so the providers it registers never become available. - Not a V1-compatibility regression: the plugin's V2 code path is what fails. The package's declared repository
(https://github.com/jaredboynton/cursor-oauth-opencode) no longer resolves, so the docs/API contract is the
actionable surface here.
Suggested resolutions
- If
catalogsis a stale leftover in the migration guide's prose, list the domains that actually exist (or drop the
sentence). - If a catalog domain is planned for the public plugin API, document it under
/build/plugins#apiand expose it on the
Promise and Effect contexts. - Publishing a model catalog appears to be possible today through
ctx.provider.transform/ctx.model.transform;
if that is the intended replacement, the migration guide should say so, because that is exactly what this plugin was
trying to do.
Frequency
Consistent — reproduces on every reload, not intermittently.
Activity
opencode-agent commentedon Oct 15, 2026
Thanks for the detailed report. I was able to reproduce this on 2.0.29.
The script below starts with a fresh config and loads one local plugin from the global
opencode.jsonc. In itssetup(), the plugin callsctx.catalog.transform(...), which is what the migration guide's "catalogs" domain suggests. It then checksGET /api/plugin. The plugin is reported asfailedwithTypeError: undefined is not an object (evaluating 'ctx.catalog.transform'). The context has nocatalogkey at all. It hasprovider,model,command,integration,mcp,reference,skill,storage,vcs,worktree,websearchand others.This still happens on the latest
dev(7ced591c), and/build/plugins/migrate-v1there still lists "catalogs". It looks like the catalog domain was split into separate provider and model registries in #48901, and the guide wasn't updated to match.As you suggested,
ctx.provider.transform/ctx.model.transformlook like the current way to publish providers and models from a plugin.Reproduction script (repro.sh)
Run it:
bash repro.shKeenaNet commentedon Oct 15, 2026
Additional evidence that narrows this down: this is not stale prose in the guide, it is an undocumented rename of a
first-class domain.
@opencode-ai/plugin@1.18.18(the V1 SDK, which also ships a V2 plugin API) types the member explicitly:@opencode/plugin@2.0.29has nocatalogdomain, but itsProviderEditorandModelEditorcarry the same operationsas
CatalogDraft.providerandCatalogDraft.model(plusadd), and they are now top-levelctx.provider/ctx.modeldomains with their owntransformandreload.So
ctx.catalog.transform(...)andctx.catalog.reload()no longer exist under that name — and a plugin author codingagainst the 1.18.x V2 API had a typed
ctx.catalogto target.What is missing is the migration step. The plugin migration guide jumps from V1 hooks straight to
ctx.provider.transform/ctx.model.transformwithout ever mentioning thatctx.catalogwas renamed and split, whilethe same guide's prose still lists
catalogsas a current context domain.Suggested mapping to document:
@opencode/pluginctx.catalog.transform(draft => …)ctx.provider.transform(...)and/orctx.model.transform(...)ctx.catalog.reload()ctx.provider.reload()/ctx.model.reload()draft.provider.update(id, fn)editor.update(id, fn)onctx.providerdraft.model.update(providerID, modelID, fn)editor.update(providerID, modelID, fn)onctx.modelConcrete downstream effect, unchanged from the report:
cursor-oauth-opencode@0.22.1depends on@opencode-ai/plugin@^1.2.27, callsctx.catalogin its V2 entrypoint, and cannot load on OpenCode 2.0.29.