sinelaw/fresh · error
Plugin ' ' not found
Error message
Plugin '{}' not found What it means
Raised by `unload_plugin_internal` when the given plugin name is not a key in the loaded-plugins `HashMap`. The runtime only unloads plugins it has previously loaded (removing their commands, i18n strings, and runtime state); requesting an unload for an unknown name returns this error instead of silently succeeding.
Solutions
- Check that the plugin is currently loaded (name exactly matches its file stem) before calling unload.
- Treat 'not found' on unload as idempotent success if your flow may unload twice.
- Verify the name used at load time matches; fix any normalization (case, hyphen/underscore) differences.
- If the plugin failed to load earlier, clean up its state via the load error path instead of calling unload.
Example fix
// before
unload_plugin_internal(runtime, &mut plugins, "My-Plugin").await?;
// after
if plugins.contains_key("my-plugin") {
unload_plugin_internal(runtime, &mut plugins, "my-plugin").await?;
} Defensive patterns
Strategy: try-catch
Validate before calling
if !plugins.contains_key(name) {
eprintln!("plugin '{}' is not loaded; skipping unload", name);
return Ok(());
} Try / catch
if let Err(e) = unload_plugin_internal(Rc::clone(&runtime), &mut plugins, name).await {
if e.to_string().contains("not found") {
tracing::debug!("unload ignored: plugin '{}' not loaded", name); // treat as idempotent
} else {
return Err(e);
}
} Prevention
- Track loaded plugin names in one place and only unload from that registry.
- Make unload idempotent by checking membership first.
- Use the exact file stem as the plugin name everywhere (no case/separator transforms).
- Don't call unload after a failed load — the plugin was never registered.
When it happens
Trigger: Calling the unload API (directly or via a plugin-management command) with a `name` that was never loaded, already unloaded, or spelled differently than the registered name (the filename stem is the key).
Common situations: Double-unload after an earlier unload or failed load; unloading after a reload that renamed the plugin; name mismatch between the requested name and the file stem (e.g. `my_plugin` vs `my-plugin`); unloading a plugin whose initial load failed so it never entered the map.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/43543f969c47705d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-plugin-runtime/src/thread.rs:1952
// Unregister i18n strings
runtime
.borrow_mut()
.services
.unregister_plugin_strings(name);
// Remove all commands registered by this plugin
runtime
.borrow()
.services
.unregister_commands_by_plugin(name);
// Clean up plugin runtime state (context, event handlers, actions, callbacks)
runtime.borrow().cleanup_plugin(name);
Ok(())
} else {
Err(anyhow!("Plugin '{}' not found", name))
}
}
/// Reload a plugin
async fn reload_plugin_internal(
runtime: Rc<RefCell<QuickJsBackend>>,
plugins: &mut HashMap<String, TsPluginInfo>,
name: &str,
) -> Result<()> {
let path = plugins
.get(name)
.ok_or_else(|| anyhow!("Plugin '{}' not found", name))?
.path
.clone();
unload_plugin_internal(Rc::clone(&runtime), plugins, name)?;
load_plugin_internal(runtime, plugins, &path).await?;
View on GitHub (pinned to 67894ca546)