Repository navigation
Description
Description
A plugin that default-exports an async function — the shape that the published
@opencode-ai/plugin root entry types as Plugin — is rejected by the v2 plugin
loader, which requires default to be an object with id plus effect or setup.
The type-level API and the runtime contract disagree, so the plugin compiles cleanly and
then fails only at load time with a WARN.
Log (~/.local/share/opencode/log/opencode.log, line 220):
2026-09-22T09:41:03.255Z level=WARN run=0ac7ee47 message="failed to load plugin" target=opencode-insights ref=err_1c6a9b92 cause="Cause([Fail(PluginModule.LoadError: Plugin must export a default definition with an id and an effect or setup function. (cause: SchemaError(Expected object\n at [\"default\"])))])" role=server
Preceded at line 219 by loading plugin ... entrypoint=file://.../opencode-insights/dist/index.js.
Two load attempts failed identically (ref=err_64bddb52, ref=err_1c6a9b92). There are no
level=ERROR lines — the failure is WARN-only, so the plugin's /insights command and its
insights_collect / insights_generate tools are silently unavailable.
The three conflicting contracts
1. Runtime (v2.0.12 binary) — requires an object. Recovered from the bundled loader
(minified identifiers renamed for readability; schema structure verbatim):
const _D = z.object({
default: z.union([
z.object({ id: z.string(), effect: z.function() }),
z.object({ id: z.string(), setup: z.function() }),
]),
});
const v = validate(_D)(mod).default;
const w = "effect" in v ? v : adaptToEffect(v);Because _D is applied to mod.default before the "effect" in v / adaptToEffect(v)
fallback, a function default export fails validation and can never reach the adapter.
2. SDK root types — advertise a function. @opencode-ai/plugin@1.18.32,
dist/index.d.ts:51:
export type Plugin = (input: PluginInput, options?: PluginOptions) => Promise<Hooks>;The root entry's runtime is only 27 bytes — export * from "./tool.js"; — so define is
not importable from "@opencode-ai/plugin". The correct object shapes live behind
subpaths:
@opencode-ai/plugin/v2/promise→interface Plugin { readonly id: string; readonly setup: (ctx) => Promise<void> | void }@opencode-ai/plugin/v2/effect→interface Plugin { readonly id: string; readonly effect: (ctx) => Effect<void, never, R> }
3. The SDK's own bundled example — would be rejected too. dist/example-workspace.js:
export const FolderWorkspacePlugin = async ({ experimental_workspace }) => { /* ... */ return {}; };
export default FolderWorkspacePlugin; // function -> SchemaError at ["default"]An author copying the shipped example, or typing against the root Plugin, gets zero
compile errors and a load failure at runtime.
Why this plugin fails
opencode-insights@2026.03.04:
// dist/index.d.ts
import type { Plugin } from "@opencode-ai/plugin";
declare const InsightsPlugin: Plugin;
export default InsightsPlugin;// dist/index.js
const InsightsPlugin = async (ctx) => { /* registers /insights + 2 tools */ };
export default InsightsPlugin;Type-checking passes against Plugin (a function). The loader rejects it because
default is not an object.
This is not only a stale-dependency problem: the plugin bundles
@opencode-ai/plugin@1.2.15, but rebuilding against the current 1.18.32 root entry would
still type Plugin as a function and still fail. Pinning a newer SDK does not fix it.
Secondary DX issue
The surfaced cause is SchemaError(Expected object at ["default"]). It does not say that a
function was found, nor point at Plugin.define or the v2/promise / v2/effect
subpaths, so the only way to diagnose it is to recover the schema from the binary.
Expected Behavior
Either:
- the loader accepts the function form documented by the root
Plugintype (there is
already anadaptToEffectbranch after validation, suggesting wider shapes were
intended), or - the root
Plugintype anddist/example-workspace.jsare updated to the object form so
they agree with the loader,
and the error names the found type and the required migration, e.g.
default export is a function; expected an object — use define() from "@opencode-ai/plugin/v2/promise".
Actual Behavior
WARN failed to load plugin + SchemaError(Expected object at ["default"]). The plugin's
/insights command and insights_collect / insights_generate tools are silently
unavailable.
Additional Context
Overlapping but distinct reports:
- beta: V1 plugins unloadable with no migration path, custom commands unreachable headless, no V2 plugin/API docs for agents #48365 — V1 plugins unloadable, no migration path (broader; does not cover the SDK type/example advertising the rejected shape)
- Plugins using the exported v1
{ id, server }module shape fail to load, silently #42878 — the{ id, server }module shape - Plugin loader throws TypeError or executes arbitrary named utility exports in legacy plugins #50021 —
TypeError/ executing named utility exports in legacy plugins
This issue is specifically that the current published SDK's root types and bundled
example steer authors into a shape the current loader rejects.
Reproducible consistently (2/2 load attempts within a single boot).
Plugins
opencode-insights@2026.03.04 — declared in project opencode.json as
"plugin": ["opencode-insights"]. No plugins in global config; 12 built-in reconciled per
run, opencode-insights is the only plugin load attempt.
OpenCode version
opencode v2.0.12 (@opencode/cli@2.0.12)
Steps to reproduce
- Add
"plugin": ["opencode-insights"]toopencode.json. - Start the OpenCode server.
- Observe
loading pluginimmediately followed byfailed to load pluginin
~/.local/share/opencode/log/opencode.log.
Minimal repro without the third-party package — any module whose default export is a
function:
// plugin.js
export default async (ctx) => ({ tool: {} });Expected: loads, or errors with a migration pointer. Actual:
SchemaError(Expected object at ["default"]).
Screenshot and/or share link
None. The exact log line is pasted in Description (line 220 of
~/.local/share/opencode/log/opencode.log, ref=err_1c6a9b92).
Operating System
Debian GNU/Linux 11 (bullseye) — Linux 6.8.0-1064-azure x86_64
Terminal
VS Code integrated terminal (TERM_PROGRAM=vscode, TERM=xterm-256color, COLORTERM=truecolor); shell /bin/bash
Plugins
No response
OpenCode version
2.0.12
Steps to reproduce
- install opencode on codespace
- start opencode - plugin error directly on start
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response
Activity
famewolf commentedon Sep 24, 2026
Workaround evidence from v1 (2026-09-23):
Confirmed the same plugin (a function default-export plugin) loads and works correctly on v1.18.31. The v2 loader rejects it with the schema error described in this issue.
For plugins that must work on both v1 and v2, the only current workaround is to compile/bundle the TypeScript to JavaScript (e.g., via
bun build) and export an object with{ id, setup }— matching what the v2 loader actually accepts at runtime, even though the published@opencode-ai/plugintypes sayPluginis a function.This is fragile: the type definitions and the runtime contract disagree, so plugins that compile cleanly against the published types fail only at load time.
bad1987 commentedon Oct 19, 2026
Confirmed on
opencode v2.0.32with a currently published plugin, so this is not limited to an older SDK build.Plugin:
nia-opencode@latest, which resolves to0.1.7(published 2026-01-14, still the latest release).The package depends on
@opencode-ai/plugin^1.0.162and its compiled entrypoint ends with:Log entry from
~/.local/share/opencode/log/opencode.log:The loader resolves the package to
~/.cache/opencode/node_modules/nia-opencode/dist/index.js, logsloading plugin, then rejects the function default export at schema validation. Plugin setup never runs, so this is not an API key, network, or Nia service failure.I also see the same rejection for a local
~/.config/opencode/plugins/code-review.tsV1 plugin, with the sameExpected object at ["default"]cause.Environment:
TERM=xterm-256color,COLORTERM=truecolor/bin/bashnia-opencode@latest, localcode-review.tsThe package needs a V2 port, but the OpenCode-side improvement from this issue still applies: the warning does not name the type that was found or link to the V2 plugin migration guide. A message like
default export is a function; expected an object with id and setup or effectwould make this diagnosable without reading the loader schema.Workaround used here: remove the V1 plugin from the V2 config, or port it to
Plugin.define({ id, setup })and mapchat.messagetoctx.session.hook("prompt", ...).neriousy commentedon Oct 20, 2026
The types and example cited here come from
@opencode-ai/plugin1.x and describe the V1 plugin API. V1 plugins are not compatible with OpenCode V2; the plugin API change is intentional, so compiling against the V1 types does not establish compatibility with the V2 loader.Plugin authors need to port their implementations to the V2 API. Please report migration requests for
opencode-insights,nia-opencode, or other third-party plugins to their respective maintainers. Migration guidance: https://opencode.ai/v2/docs/build/plugins/migrate-v1Closing as expected V1/V2 incompatibility, rather than a V2 loader/type-contract bug.