Skip to content

Plugin not loaded by OpenCode 1.17.x — default export must be V1 PluginModule {id, server} #1

Description

@levdesign-levia

OpenCode version affected: 1.17.9

SDK contract

From @opencode-ai/plugin/dist/index.d.ts (SDK 1.17.11, used by OpenCode CLI 1.17.9):

export type Plugin = (input: PluginInput, options?: PluginOptions) => Promise<Hooks>;
export type PluginModule = {
  id?: string;
  server: Plugin;
  tui?: never;
};

OpenCode's plugin loader expects the V1 default export to satisfy PluginModule, i.e. it must expose a .server property that is the actual plugin function.

Actual oc-sync export shape

oc-sync/dist/index.js ships:

import { OpencodeSyncPlugin } from './plugin/index.js';
// Only export the plugin as default - OpenCode expects a single default export
export default OpencodeSyncPlugin;

The default export is a bare async function (Plugin), not a { server: Plugin } object. Runtime checks confirm this:

const m = await import('file:///.../node_modules/oc-sync/dist/index.js');
typeof m.default;      // 'function'
m.default?.server;     // undefined
Object.keys(m);        // ['default']

The function itself works perfectly when called directly — the failure is purely a contract mismatch with the OpenCode loader.

Working example

@slkiser/opencode-quota loads correctly because its default export matches the contract:

{ id: '@slkiser/opencode-quota', server: QuotaToastPlugin }

Suggested fix

Change the default export in src/index.ts from:

export default OpencodeSyncPlugin;

to:

import { OpencodeSyncPlugin } from './plugin/index.js';

const plugin: PluginModule = {
  id: 'oc-sync',
  server: OpencodeSyncPlugin,
};

export default plugin;

(or equivalent for the current SDK contract).

Local shim workaround

Until this is fixed upstream, users can wrap oc-sync in a local V1 shim and reference that path in opencode.json:

// oc-sync-v1-shim.ts
import oc from 'oc-sync';

const plugin = { id: 'oc-sync', server: oc as any };
export default plugin;

Then register the absolute path to that file in the plugin array instead of the oc-sync package name.

Thanks for the useful plugin — happy to help test a fixed release.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions