Bug Report
Environment
- OpenCode version: 1.1.60 (installed via
curl -fsSL https://opencode.ai/install | bash)
- OS: Linux 6.6.87.2-microsoft-standard-WSL2 x86_64
- Bun: 1.3.8
@opencode-ai/plugin: 1.1.60
Error Message
TypeError: hook["event"] is not a function. (In 'hook["event"]?.({ event: input })', 'hook["event"]' is an instance of Object)
This fires on every Bus event (e.g., file.edited), not just once. The error is non-blocking — edits succeed — but it spams the logs.
Plugin Code
The plugin is a local file at .opencode/plugins/are-check-update.js, auto-discovered by OpenCode. It follows the exact pattern from the official plugin docs:
import { existsSync, mkdirSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
import { spawn } from 'child_process';
export const AreCheckUpdate = async (ctx) => {
return {
event: async ({ event }) => {
if (event.type !== 'session.created') return;
// ... spawns background update check ...
},
};
};
- Single named export, no
export default
- Returns
{ event: async (...) => {...} } which conforms to the Hooks interface
- No other plugins are installed (only the built-in ones: CodexAuth, CopilotAuth, AnthropicAuth, GitlabAuth)
Analysis
The error originates from Plugin.init() in packages/opencode/src/plugin/index.ts:
Bus.subscribeAll(async (input) => {
const hooks = await state().then((x) => x.hooks);
for (const hook of hooks) {
hook["event"]?.({
event: input,
});
}
});
Optional chaining ?.() only guards against undefined/null. If hook["event"] exists but is an Object (not a function), it throws TypeError.
I verified the plugin works correctly in standalone Bun:
$ bun -e "
const mod = await import('file:///path/to/.opencode/plugins/are-check-update.js');
const result = await mod.AreCheckUpdate({});
console.log(typeof result.event); // 'function'
console.log(result.event.constructor.name); // 'AsyncFunction'
result.event({ event: { type: 'file.edited' } }); // no error
"
None of the built-in plugins (CodexAuth, CopilotAuth, AnthropicAuth, GitlabAuth) have an event property in their returned hooks — they only use auth, chat.headers, and experimental.chat.system.transform. So our plugin is the only source of an event key in the hooks array.
Relationship to #11392
This appears related to #11392 (fn3 is not a function, fn3 is an instance of Array), which was a similar type confusion in the plugin hook execution path. That issue involved export default causing duplicate entries. Our plugin does NOT use export default, but the error pattern is the same — a hook property that should be a function is somehow resolved as a non-callable Object at runtime inside the compiled binary.
Expected Behavior
hook["event"]?.({ event: input }) should call the async function without error, or at minimum not throw on every Bus event.
Suggested Fix
Add a typeof guard in Plugin.init():
Bus.subscribeAll(async (input) => {
const hooks = await state().then((x) => x.hooks);
for (const hook of hooks) {
const fn = hook["event"];
if (typeof fn === "function") {
fn({ event: input });
}
}
});
This would prevent the TypeError regardless of how the property gets resolved.
Bug Report
Environment
curl -fsSL https://opencode.ai/install | bash)@opencode-ai/plugin: 1.1.60Error Message
This fires on every Bus event (e.g.,
file.edited), not just once. The error is non-blocking — edits succeed — but it spams the logs.Plugin Code
The plugin is a local file at
.opencode/plugins/are-check-update.js, auto-discovered by OpenCode. It follows the exact pattern from the official plugin docs:export default{ event: async (...) => {...} }which conforms to theHooksinterfaceAnalysis
The error originates from
Plugin.init()inpackages/opencode/src/plugin/index.ts:Optional chaining
?.()only guards againstundefined/null. Ifhook["event"]exists but is an Object (not a function), it throwsTypeError.I verified the plugin works correctly in standalone Bun:
None of the built-in plugins (CodexAuth, CopilotAuth, AnthropicAuth, GitlabAuth) have an
eventproperty in their returned hooks — they only useauth,chat.headers, andexperimental.chat.system.transform. So our plugin is the only source of aneventkey in the hooks array.Relationship to #11392
This appears related to #11392 (
fn3 is not a function, fn3 is an instance of Array), which was a similar type confusion in the plugin hook execution path. That issue involvedexport defaultcausing duplicate entries. Our plugin does NOT useexport default, but the error pattern is the same — a hook property that should be a function is somehow resolved as a non-callable Object at runtime inside the compiled binary.Expected Behavior
hook["event"]?.({ event: input })should call the async function without error, or at minimum not throw on every Bus event.Suggested Fix
Add a
typeofguard inPlugin.init():This would prevent the TypeError regardless of how the property gets resolved.