can1357/oh-my-pi · error · TypeError

pluginClass must be a MnemopiPlugin subclass

Error message

pluginClass must be a MnemopiPlugin subclass

What it means

registerPlugin validates that the second argument is a constructor function whose prototype chain derives from MnemopiPlugin. The TypeError fires when you pass anything else — a plain class, a function, or a non-function value — because the manager relies on MnemopiPlugin's interface for initialize/shutdown lifecycle calls.

Source

Thrown at packages/mnemopi/src/core/plugins.ts:273

	override onRecall(_memory: MemoryDict): void {}
	override onConsolidate(_summary: MemoryDict): void {}
	override onInvalidate(_memoryId: string): void {}
}

export type PluginConstructor<T extends MnemopiPlugin = MnemopiPlugin> = new (config?: PluginConfig) => T;

export class PluginManager {
	private readonly registry = new Map<string, PluginConstructor>();
	private readonly instances = new Map<string, MnemopiPlugin>();
	constructor(private readonly pluginDir = DEFAULT_PLUGIN_DIR) {
		this.registerPlugin("logging", LoggingPlugin);
		this.registerPlugin("metrics", MetricsPlugin);
		this.registerPlugin("filter", FilterPlugin);
		this.registerPlugin("compression", CompressionPlugin);
	}
	registerPlugin(name: string, pluginClass: PluginConstructor): void {
		if (typeof pluginClass !== "function" || !(pluginClass.prototype instanceof MnemopiPlugin)) {
			throw new TypeError("pluginClass must be a MnemopiPlugin subclass");
		}
		if (this.registry.has(name)) throw new ValueError(`Plugin '${name}' is already registered`);
		this.registry.set(name, pluginClass);
	}
	loadPlugin(name: string, config: PluginConfig = {}): MnemopiPlugin {
		const pluginClass = this.registry.get(name);
		if (pluginClass === undefined) throw new ValueError(`Plugin '${name}' is not registered`);
		if (this.instances.has(name)) throw new Error(`Plugin '${name}' is already loaded`);
		const instance = new pluginClass(config);
		instance.initialize();
		this.instances.set(name, instance);
		return instance;
	}
	unloadPlugin(name: string): void {
		const instance = this.instances.get(name);
		if (instance === undefined) throw new ValueError(`Plugin '${name}' is not loaded`);
		this.instances.delete(name);
		instance.shutdown();

View on GitHub (pinned to 9690622007)

Solutions

  1. Make the plugin class extend MnemopiPlugin: `class MyPlugin extends MnemopiPlugin { ... }`
  2. Verify the import resolves to the class itself (console.log(pluginClass) before registering; fix default/named import)
  3. Pass a class constructor, not an instance or a factory function that returns one

Example fix

// before
manager.registerPlugin("custom", createCustomPlugin);
// after
class CustomPlugin extends MnemopiPlugin { /* ... */ }
manager.registerPlugin("custom", CustomPlugin);
Defensive patterns

Strategy: type-guard

Validate before calling

import { MnemopiPlugin } from "...";
if (typeof pluginClass === "function" && pluginClass.prototype instanceof MnemopiPlugin) {
  manager.registerPlugin(name, pluginClass);
}

Type guard

function isPluginConstructor(value: unknown): value is new (config: PluginConfig) => MnemopiPlugin {
  return typeof value === "function" && value.prototype instanceof MnemopiPlugin;
}

Try / catch

try {
  manager.registerPlugin(name, pluginClass);
} catch (err) {
  if (err instanceof TypeError) {
    throw new Error(`registerPlugin(${name}): not a MnemopiPlugin subclass`, { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling manager.registerPlugin(name, X) where X is a plain object, an arrow function, a class not extending MnemopiPlugin, or a mis-typed import (e.g. passing the module namespace instead of the class).

Common situations: Importing the plugin class incorrectly (default vs named import) so the value is undefined; writing a custom plugin that forgets `extends MnemopiPlugin`; refactoring that replaced the class with a factory function.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/a439c9e96da5f708. Report an issue: GitHub.