FyroxEngine/Fyrox · error
Unable to reload dynamic plugins. Reason
Error message
Unable to reload dynamic plugins. Reason: {message} What it means
Fyrox logs this when `reload_dynamic_plugins` fails during hot-reloading of dynamic plugin libraries. The plugin .so/.dll could not be reloaded (compile error, file lock, missing export symbols), so the previously loaded plugin version stays active and hot reload is skipped for that iteration.
Solutions
- Read the logged `{message}` — it names the specific load failure (missing file, bad library, missing symbols)
- Wait for the build to fully finish before reload (add a debounce or wait on cargo's exit)
- On Windows, copy the built plugin to a new filename and load that, avoiding locks on the original
- Rebuild the plugin and verify it exports the required `fyrox::plugin::PluginConstructor` entry points
- Ensure the dynamic-plugins feature and matching fyrox/fyrox-core versions are used for plugin and host
Example fix
// before: reloading while cargo still writing the dylib plugin_watcher.on_change(|_| engine.update_hot_reload()); // after: debounce until the build output stabilizes plugin_watcher.on_change_debounced(Duration::from_millis(500), |_| engine.update_hot_reload());
Defensive patterns
Strategy: retry
Validate before calling
fn plugin_dylib_ready(path: &Path) -> bool {
match std::fs::metadata(path) {
Ok(m) => m.len() > 0
&& m.modified().ok().map(|t| t.elapsed().unwrap_or_default()).unwrap_or_default()
> std::time::Duration::from_millis(250),
Err(_) => false,
}
} Try / catch
// Retry the reload on the next tick after a failure
if let Err(message) = engine.update_hot_reload() {
log::warn!("plugin reload deferred: {message}");
schedule_retry(Duration::from_secs(1));
} Prevention
- Debounce file-watch events so reload happens after the compiler finishes
- On Windows, load a copy of the dylib to avoid build locks
- Pin identical fyrox versions between host and plugin crates
- Verify plugin entry-point exports after refactoring
When it happens
Trigger: Running with dynamic-plugins hot reloading enabled and calling the engine's `update_hot_reload`/plugin-reload path with dynamic libraries: the plugin .dll/.so is still being written by the compiler, is locked by the OS, was deleted, or fails to load/link.
Common situations: Cargo/rustc has not finished emitting the new plugin binary when the watcher fires; Windows file locks on the dll; plugin renamed or output path changed in Cargo.toml; deploying a plugin built for a different ABI/compiler version.
Related errors
- Unable to create resource watcher. Reason
- Graphics context is uninitialized!
- only rectangle textures can be used as render target!
- Unsupported platform!
- Cannot obtain a reference to the plugin, because it is…
AI-assisted analysis of FyroxEngine/Fyrox@76c91aad8e (2026-09-10).
Data as JSON: /api/errors/9a18b5fa115b14ae.
Report an issue: GitHub.
Appendix: source
Thrown at fyrox-impl/src/engine/mod.rs:1665
///
/// ## Platform-specific
///
/// - Windows, Unix-like systems (Linux, macOS, FreeBSD, etc) - fully supported.
/// - WebAssembly - not supported
/// - Android - not supported
pub fn handle_plugins_hot_reloading<F>(
&mut self,
#[allow(unused_variables)] dt: f32,
#[allow(unused_variables)] controller: ApplicationLoopController,
#[allow(unused_variables)] lag: &mut f32,
#[allow(unused_variables)] on_reloaded: F,
) where
F: FnMut(&dyn Plugin),
{
#[cfg(any(unix, windows))]
{
if let Err(message) = self.reload_dynamic_plugins(dt, controller, lag, on_reloaded) {
Log::err(format!(
"Unable to reload dynamic plugins. Reason: {message}"
))
}
}
}
/// Performs pre update for the engine.
///
/// Normally, this is called from `Engine::update()`.
/// You should only call this manually if you don't use that method.
///
/// ## Parameters
///
/// `lag` - is a reference to time accumulator, that holds remaining amount of time that should be used
/// to update a plugin. A caller splits `lag` into multiple sub-steps using `dt` and thus stabilizes
/// update rate. The main use of this variable, is to be able to reset `lag` when you doing some heavy
/// calculations in a your game loop (i.e. loading a new level) so the engine won't try to "catch up" with
/// all the time that was spent in heavy calculation. The engine does **not** use this variable itself,View on GitHub (pinned to 76c91aad8e)