can1357/oh-my-pi · error · ValueError
Plugin '${name}' is not loaded
Error message
Plugin '${name}' is not loaded What it means
unloadPlugin requires the named plugin to have a live instance in the instances map; otherwise it raises a ValueError. You can only unload plugins that were successfully loaded with loadPlugin.
Source
Thrown at packages/mnemopi/src/core/plugins.ts:289
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,
loaded: this.instances.has(name),
instance: this.instances.get(name) ?? null,
});
return result;
}
getPlugin(name: string): MnemopiPlugin | null {
const loaded = this.instances.get(name);
if (loaded !== undefined) return loaded;
if (this.registry.has(name)) return this.loadPlugin(name);View on GitHub (pinned to 9690622007)
Solutions
- Check that the plugin was loaded before unloading (track your own loaded set or use the instances/registry accessor)
- Make cleanup idempotent: wrap unloadPlugin in a check or catch the ValueError and ignore it
- Fix the load path so the plugin is actually loaded before teardown runs
Example fix
// before
manager.unloadPlugin("metrics"); // throws if never loaded
// after
try {
manager.unloadPlugin("metrics");
} catch (err) {
if (!(err instanceof ValueError)) throw err; // already unloaded — fine
} Defensive patterns
Strategy: try-catch
Validate before calling
// only unload names you successfully loaded
const loadedNames = new Set<string>();
loadedNames.add(manager.loadPlugin("metrics") && "metrics");
if (loadedNames.has("metrics")) manager.unloadPlugin("metrics"); Try / catch
function unloadSafe(name: string) {
try {
manager.unloadPlugin(name);
} catch (err) {
if (err instanceof ValueError && err.message.includes("not loaded")) return; // idempotent teardown
throw err;
}
} Prevention
- Make shutdown/teardown idempotent by catching the not-loaded case
- Only unload plugins your code explicitly loaded
- Never assume every registered plugin has an instance
When it happens
Trigger: Calling manager.unloadPlugin("metrics") when "metrics" was registered but never loaded; unloading after a previous unloadPlugin already removed it; unloading a name that only exists in the registry.
Common situations: Teardown/cleanup code assuming all registered plugins were loaded; double-stop paths (shutdown handler plus explicit unload); loading failures earlier meant the instance never existed.
Related errors
- Plugin '${name}' is not registered
- Plugin '${name}' is already loaded
- DAP adapter ${this.adapter.name} is not running
- Debug session ${root.id} is still active. Terminate it befor
- Cannot ${action} on a disposed JS runtime
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e311e5d03157e52a.
Report an issue: GitHub.