{"record":{"id":"f0ca5afb8b2262a2","repo":"sinelaw/fresh","slug":"plugin-thread-shut-down","errorCode":null,"errorMessage":"Plugin thread shut down","messagePattern":"Plugin thread shut down","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-plugin-runtime/src/thread.rs","lineNumber":574,"sourceCode":"            .lock()\n            .ok()\n            .and_then(|mut owners| owners.remove(&request_id));\n        if let Some(plugin_name) = plugin_name {\n            if let Some(sender) = self.request_sender.as_ref() {\n                fire_and_forget(sender.send(PluginRequest::TrackAsyncResource {\n                    plugin_name,\n                    resource,\n                }));\n            }\n        }\n    }\n\n    /// Load a plugin from a file (blocking)\n    pub fn load_plugin(&self, path: &Path) -> Result<()> {\n        let (tx, rx) = oneshot::channel();\n        self.request_sender\n            .as_ref()\n            .ok_or_else(|| anyhow!(\"Plugin thread shut down\"))?\n            .send(PluginRequest::LoadPlugin {\n                path: path.to_path_buf(),\n                response: tx,\n            })\n            .map_err(|_| anyhow!(\"Plugin thread not responding\"))?;\n\n        rx.recv().map_err(|_| anyhow!(\"Plugin thread closed\"))?\n    }\n\n    /// Load all plugins from a directory (blocking)\n    pub fn load_plugins_from_dir(&self, dir: &Path) -> Vec<String> {\n        let (tx, rx) = oneshot::channel();\n        let Some(sender) = self.request_sender.as_ref() else {\n            return vec![\"Plugin thread shut down\".to_string()];\n        };\n        if sender\n            .send(PluginRequest::LoadPluginsFromDir {\n                dir: dir.to_path_buf(),","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-plugin-runtime/src/thread.rs#L556-L592","documentation":"load_plugin() sends a LoadPlugin request over a oneshot channel to a dedicated plugin worker thread. This error is raised when the runtime's `request_sender` is `None`, meaning the plugin thread has already been shut down (e.g. `shutdown()` was called or the handle was created after teardown) and no receiver exists to accept the request. The library refuses to dispatch work to a dead worker rather than hang or silently drop the load.","triggerScenarios":"Calling `PluginRuntimeHandle::load_plugin(path)` after the plugin thread has been stopped (`request_sender` set to `None`), or on a handle whose worker thread already exited/panicked and was joined.","commonSituations":"Shutting the editor/plugin runtime down while a background task still tries to load a plugin; calling load_plugin from a drop path or destructor that runs after shutdown; reusing a stale handle across application restart phases.","solutions":["Ensure the plugin thread is started (and not yet shut down) before calling load_plugin; check the runtime's lifecycle ordering.","Guard call sites so plugin loading is skipped once shutdown has begun (e.g. an AtomicBool 'shutting_down' checked before load_plugin).","If the thread exited unexpectedly, recreate the runtime handle by spawning a new plugin thread instead of reusing the old handle.","Match on this error and surface a clear 'plugin runtime unavailable' message instead of retrying."],"exampleFix":"// before\nruntime.shutdown();\nruntime.load_plugin(&plugin_path)?;\n\n// after\nif !runtime.is_shutdown() {\n    runtime.load_plugin(&plugin_path)?;\n} else {\n    eprintln!(\"plugin runtime already shut down; skipping load of {}\", plugin_path.display());\n}","handlingStrategy":"try-catch","validationCode":"// Rust\nfn can_load(rt: &PluginRuntimeHandle, shutting_down: &AtomicBool) -> bool {\n    !shutting_down.load(Ordering::SeqCst) && rt.is_running()\n}","typeGuard":"fn is_live(sender: &Option<Sender<PluginRequest>>) -> bool { sender.is_some() }","tryCatchPattern":"match rt.load_plugin(&path) {\n    Ok(()) => { /* plugin loaded */ }\n    Err(e) if e.to_string().contains(\"Plugin thread shut down\") => {\n        warn!(\"plugin runtime stopped; not loading {}\", path.display());\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Establish a strict lifecycle: start thread -> load plugins -> ... -> unload -> shutdown.","Expose and check an is_running()/is_shutdown() flag before every plugin API call.","Never reuse a runtime handle after shutdown; create a new one if needed.","Add a shutdown barrier so background tasks finish before teardown."],"tags":["plugin-runtime","lifecycle","shutdown","threading"],"backgroundTag":"thread-interrupted","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}