{"record":{"id":"9a18b5fa115b14ae","repo":"FyroxEngine/Fyrox","slug":"unable-to-reload-dynamic-plugins-reason-message","errorCode":null,"errorMessage":"Unable to reload dynamic plugins. Reason: {message}","messagePattern":"Unable to reload dynamic plugins\\. Reason: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fyrox-impl/src/engine/mod.rs","lineNumber":1665,"sourceCode":"    ///\n    /// ## Platform-specific\n    ///\n    /// - Windows, Unix-like systems (Linux, macOS, FreeBSD, etc) - fully supported.\n    /// - WebAssembly - not supported\n    /// - Android - not supported\n    pub fn handle_plugins_hot_reloading<F>(\n        &mut self,\n        #[allow(unused_variables)] dt: f32,\n        #[allow(unused_variables)] controller: ApplicationLoopController,\n        #[allow(unused_variables)] lag: &mut f32,\n        #[allow(unused_variables)] on_reloaded: F,\n    ) where\n        F: FnMut(&dyn Plugin),\n    {\n        #[cfg(any(unix, windows))]\n        {\n            if let Err(message) = self.reload_dynamic_plugins(dt, controller, lag, on_reloaded) {\n                Log::err(format!(\n                    \"Unable to reload dynamic plugins. Reason: {message}\"\n                ))\n            }\n        }\n    }\n\n    /// Performs pre update for the engine.\n    ///\n    /// Normally, this is called from `Engine::update()`.\n    /// You should only call this manually if you don't use that method.\n    ///\n    /// ## Parameters\n    ///\n    /// `lag` - is a reference to time accumulator, that holds remaining amount of time that should be used\n    /// to update a plugin. A caller splits `lag` into multiple sub-steps using `dt` and thus stabilizes\n    /// update rate. The main use of this variable, is to be able to reset `lag` when you doing some heavy\n    /// calculations in a your game loop (i.e. loading a new level) so the engine won't try to \"catch up\" with\n    /// all the time that was spent in heavy calculation. The engine does **not** use this variable itself,","sourceCodeStart":1647,"sourceCodeEnd":1683,"githubUrl":"https://github.com/FyroxEngine/Fyrox/blob/76c91aad8eca488ce527b1af707be8b3b24ad72d/fyrox-impl/src/engine/mod.rs#L1647-L1683","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: reloading while cargo still writing the dylib\nplugin_watcher.on_change(|_| engine.update_hot_reload());\n\n// after: debounce until the build output stabilizes\nplugin_watcher.on_change_debounced(Duration::from_millis(500), |_| engine.update_hot_reload());","handlingStrategy":"retry","validationCode":"fn plugin_dylib_ready(path: &Path) -> bool {\n    match std::fs::metadata(path) {\n        Ok(m) => m.len() > 0\n            && m.modified().ok().map(|t| t.elapsed().unwrap_or_default()).unwrap_or_default()\n                > std::time::Duration::from_millis(250),\n        Err(_) => false,\n    }\n}","typeGuard":null,"tryCatchPattern":"// Retry the reload on the next tick after a failure\nif let Err(message) = engine.update_hot_reload() {\n    log::warn!(\"plugin reload deferred: {message}\");\n    schedule_retry(Duration::from_secs(1));\n}","preventionTips":["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"],"tags":["plugins","hot-reload","dynamic-loading","fyrox"],"backgroundTag":"module-init-failed","analyzedSha":"76c91aad8eca488ce527b1af707be8b3b24ad72d","analyzedAt":"2026-09-10T16:04:01.633Z","contentChangedAt":"2026-09-10T16:04:01.633Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}