can1357/oh-my-pi · error · ValueError

Plugin '${name}' is not registered

Error message

Plugin '${name}' is not registered

What it means

loadPlugin looks the name up in the registry populated by registerPlugin; if the name was never registered, a ValueError is thrown. The registry only contains the built-ins (metrics, filter, compression) plus anything explicitly registered.

Source

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

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();
	}
	listPlugins(): Array<Record<string, unknown>> {
		const result: Array<Record<string, unknown>> = [];
		for (const [name, pluginClass] of this.registry)
			result.push({
				name,
				class: pluginClass.name,

View on GitHub (pinned to 9690622007)

Solutions

  1. Call registerPlugin(name, PluginClass) before loadPlugin for non-built-in plugins
  2. Check available names with manager.listPlugins() and correct the spelling/case
  3. Use the built-in names exactly: "metrics", "filter", "compression"

Example fix

// before
manager.loadPlugin("search"); // ValueError: not registered
// after
manager.registerPlugin("search", SearchPlugin);
manager.loadPlugin("search");
Defensive patterns

Strategy: validation

Validate before calling

const known = new Set(manager.listPlugins().map(p => p.name));
if (!known.has("search")) manager.registerPlugin("search", SearchPlugin);
manager.loadPlugin("search");

Try / catch

try {
  manager.loadPlugin(name);
} catch (err) {
  if (err instanceof ValueError && err.message.includes("not registered")) {
    console.error(`Plugin "${name}" unknown; available:`, manager.listPlugins().map(p => p.name));
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling manager.loadPlugin("search") (or getPlugin/plugin/loadAll with that name) when "search" was never passed to registerPlugin; misspelling a registered plugin name.

Common situations: Typos in plugin names; expecting all plugins to be pre-registered when only the three built-ins are; loading a plugin before calling registerPlugin on it; case mismatches ("Filter" vs "filter").

Related errors


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