Skip to content

plugin: function default export rejected by v2 loader schema (SDK root Plugin type mismatch) #50590

Closed as not planned

Description

@d-oit

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 Plugin type (there is
    already an adaptToEffect branch after validation, suggesting wider shapes were
    intended), or
  • the root Plugin type and dist/example-workspace.js are 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:

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

  1. Add "plugin": ["opencode-insights"] to opencode.json.
  2. Start the OpenCode server.
  3. Observe loading plugin immediately followed by failed to load plugin in
    ~/.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

  1. install opencode on codespace
  2. start opencode - plugin error directly on start

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. famewolf commented on Sep 24, 2026

    @famewolf

    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/plugin types say Plugin is 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.

  2. bad1987 commented on Oct 19, 2026

    @bad1987

    Confirmed on opencode v2.0.32 with a currently published plugin, so this is not limited to an older SDK build.

    Plugin: nia-opencode@latest, which resolves to 0.1.7 (published 2026-01-14, still the latest release).

    The package depends on @opencode-ai/plugin ^1.0.162 and its compiled entrypoint ends with:

    var NiaPlugin = async (ctx) => { /* returns a V1 hook map */ };
    export { src_default as default, NiaPlugin };

    Log entry from ~/.local/share/opencode/log/opencode.log:

    timestamp=2026-10-19T09:09:21.408Z level=WARN run=46c81a31
    message="failed to load plugin" target=nia-opencode@latest ref=err_196a723e
    cause="Cause([Fail(PluginModule.LoadError: Plugin must export a default definition with an id and an effect or setup function. (cause: SchemaError(Expected object at [\"default\"])))])"
    

    The loader resolves the package to ~/.cache/opencode/node_modules/nia-opencode/dist/index.js, logs loading 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.ts V1 plugin, with the same Expected object at ["default"] cause.

    Environment:

    • opencode version: 2.0.32
    • OS: Linux 6.8.0-147-generic x86_64
    • Terminal: TERM=xterm-256color, COLORTERM=truecolor
    • Shell: /bin/bash
    • Install/channel: latest release
    • Active plugins: nia-opencode@latest, local code-review.ts

    The 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 effect would 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 map chat.message to ctx.session.hook("prompt", ...).

  3. neriousy commented on Oct 20, 2026

    @neriousy
    Member

    The types and example cited here come from @opencode-ai/plugin 1.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-v1

    Closing as expected V1/V2 incompatibility, rather than a V2 loader/type-contract bug.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions