anomalyco / anomalyco/opencode

Plugins using the exported v1 `{ id, server }` module shape fail to load, silently

Open
#42,878 0 comments 0 reactions 1 assignee View on GitHub

@neriousy is already working on this.

Since Aug 16, 2026.

2.0
Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description

@opencode-ai/plugin still exports a ./v1 subpath declaring:

export type PluginModule = { id?: string; server: Plugin; tui?: never }

But the v2 external loader only accepts { id, effect } or { id, setup }, so a module written
against that published type fails to decode and is dropped. packages/core/src/config/plugin/external.ts:15-30:

const PluginModule = Schema.Struct({
  default: Schema.Union([
    Schema.Struct({ id: Schema.String, effect: Schema.declare<EffectPlugin["effect"]>(...) }),
    Schema.Struct({ id: Schema.String, setup: Schema.declare<PromisePlugin["setup"]>(...) }),
  ]),
})

Two things I think are worth separating:

1. The failure is invisible. :87 ends the per-plugin block with Effect.ignoreCause, so the
decode error never reaches the CLI. plugin list and models come back without the plugin, exit
code 0, nothing on stderr. The only trace is one WARN in ~/.local/share/opencode/log/opencode.log:

level=WARN message="failed to load plugin" target=@sunerpy/opencode-kiro-auth@0.20.6
  cause="Cause([Fail(SchemaError(Missing key at [\"default\"][\"effect\"]
  Missing key at [\"default\"][\"setup\"]))])"

The user-visible symptom is a missing provider, several layers from the cause. Same class as #41234.
I'd like to send a PR for just this part — replace ignoreCause with a tap that reports through
the same channel as other startup errors. It stands on its own whatever you decide about v1.

2. Whether { id, server } should load at all. Either the loader grows a third union member, or
./v1 shouldn't be exported from the v2 package — right now it advertises a shape that cannot load.
If you do want a shim, PluginPromise.fromPromise (packages/core/src/plugin/promise.ts) already
sets the pattern and a PluginV1.fromV1 would sit next to it. The real work is mapping the v1
Hooks surface onto v2 domains, and I wouldn't attempt that wholesale — I'd bridge a named subset
and have anything outside it fail with a precise error rather than half-load.

I read the design-review note in CONTRIBUTING, so I'm asking before writing anything for (2). Is v1
compatibility in v2 wanted, or is the intended path that everything ports to { id, setup }? If
it's the latter I'll scope the PR to (1) and make the error say so explicitly, with a migration link
if one exists.

Plugins

@sunerpy/opencode-kiro-auth@0.20.6 (targets @opencode-ai/plugin@^1.15.11; entrypoint is
export default { id: 'kiro-auth', server: ... }id is present, hence the error naming only
effect and setup)

OpenCode version

0.0.0-next-17444 (@opencode-ai/cli, next channel). @opencode-ai/plugin 0.0.0-next-17444.

Steps to reproduce
  1. npm i -g @opencode-ai/cli@next
  2. Add to ~/.config/opencode/opencode.jsonc:
    { "plugins": [{ "package": "@sunerpy/opencode-kiro-auth@0.20.6" }] }
    
  3. opencode2 plugin list | grep -c kiro0, exit 0, no error printed
  4. grep "failed to load plugin" ~/.local/share/opencode/log/opencode.log → the SchemaError above
Operating System

macOS 26.5 (25F71), arm64

Terminal

Ghostty

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.