{"record":{"id":"90e2028719358148","repo":"zellij-org/zellij","slug":"plugin-is-not-stored-in-memory","errorCode":null,"errorMessage":"Plugin is not stored in memory","messagePattern":"Plugin is not stored in memory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"zellij-server/src/plugins/plugin_loader.rs","lineNumber":165,"sourceCode":"        self.loading_indication.override_previous_error();\n        let wasm_bytes = self.plugin_config.resolve_wasm_bytes(&self.plugin_dir)?;\n        let timer = std::time::Instant::now();\n        let module = Module::new(&self.engine, &wasm_bytes)?;\n        log::info!(\n            \"Loaded plugin '{}' in {:?}\",\n            self.plugin_config.path.display(),\n            timer.elapsed()\n        );\n        Ok(module)\n    }\n    fn load_module_from_memory(&mut self) -> Result<Module> {\n        let module = self\n            .plugin_cache\n            .lock()\n            .unwrap()\n            .remove(&self.plugin_config.path) // TODO: do we still bring it back later?\n            // maybe we can forgo this dance?\n            .ok_or(anyhow!(\"Plugin is not stored in memory\"))?;\n        Ok(module)\n    }\n    fn load_plugin_instance(\n        &mut self,\n        mut store: Store<PluginEnv>,\n        instance: &Instance,\n    ) -> Result<()> {\n        let err_context = || format!(\"failed to load plugin from instance {instance:#?}\");\n        let main_user_instance = instance.clone();\n        let start_function = instance\n            .get_typed_func::<(), ()>(&mut store, \"_start\")\n            .with_context(err_context)?;\n        let load_function = instance\n            .get_typed_func::<(), ()>(&mut store, \"load\")\n            .with_context(err_context)?;\n        let mut workers = HashMap::new();\n        for function_name in instance\n            .exports(&mut store)","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/zellij-org/zellij/blob/98a0837077492d53dd252ab30bc3e43e41e504f4/zellij-server/src/plugins/plugin_loader.rs#L147-L183","documentation":"Thrown by PluginLoader::load_module_from_memory when a plugin reload expects the already-compiled wasm Module to be parked in the internal plugin_cache keyed by plugin path, but the cache has no entry. The cache is consumed with a one-shot .remove(), so the entry is only present between the first load and the first reload. A miss means the reload path was taken for a plugin that was never cached (or was already reloaded once).","triggerScenarios":"Calling start_or_reload on a plugin whose initial load went through load_module_from_disk (never inserted into plugin_cache), reloading the same plugin path twice (the first reload removed the entry), or a cache eviction/race between concurrent loads of the same path.","commonSituations":"Plugin development with `zellij --layout` hot-reload workflows; two panes loading the same plugin URL causing one to drain the cache; version change altering the cache-insertion path so file-loaded plugins skip caching.","solutions":["Ensure the plugin's first load goes through the code path that inserts the module into plugin_cache (same RunPluginOrAlias path/normalization on both loads) so the reload finds it","Avoid triggering reload of the same plugin twice; after the first reload the cache entry is gone by design (see the TODO at zellij-server/src/plugins/plugin_loader.rs:167)","If you control this code, fall back to load_module_from_disk when load_module_from_memory returns None instead of erroring","Check for concurrent loads of the identical plugin path (e.g. same plugin in tiled and floating panes) and serialize/reuse them"],"exampleFix":"// before\nlet module = self.load_module_from_memory()?;\n\n// after\nlet module = match self.load_module_from_memory() {\n    Ok(module) => module,\n    Err(_) => self.load_module_from_disk()?, // recompile instead of failing\n};","handlingStrategy":"fallback","validationCode":"// before reloading, ensure a cached module exists for this exact path\nlet cached = plugin_cache.lock().unwrap().contains_key(&plugin_config.path);\nif !cached { plugin_loader.load_module_from_disk()?; } // repopulates path for reload","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Load each plugin through a single code path so the cache key (canonicalized path) matches between initial load and reload","Never reload the same plugin path twice without an intervening load; the cache entry is consumed on first use"],"tags":["plugin","wasm","cache","reload","plugin-loader"],"backgroundTag":null,"analyzedSha":"98a0837077492d53dd252ab30bc3e43e41e504f4","analyzedAt":"2026-08-16T13:02:01.396Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}