{"record":{"id":"5d42c4b9cad1b7fa","repo":"zellij-org/zellij","slug":"failed-to-load-plugin-from-disk","errorCode":null,"errorMessage":"failed to load plugin from disk","messagePattern":"failed to load plugin from disk","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"zellij-utils/src/input/plugins.rs","lineNumber":129,"sourceCode":"            plugin_dir.join(&self.path).with_extension(\"wasm\"),\n        ];\n        #[cfg(not(target_family = \"wasm\"))]\n        paths.push(\n            crate::home::system_data_dir()\n                .join(\"plugins\")\n                .join(&self.path)\n                .with_extension(\"wasm\"),\n        );\n        // Throw out dupes, because it's confusing to read that zellij checked the same plugin\n        // location multiple times. Do NOT sort the vector here, because it will break the lookup!\n        paths.dedup();\n\n        // This looks weird and usually we would handle errors like this differently, but in this\n        // case it's helpful for users and developers alike. This way we preserve all the lookup\n        // errors and can report all of them back. We must initialize `last_err` with something,\n        // and since the user will only get to see it when loading a plugin failed, we may as well\n        // spell it out right here.\n        let mut last_err: Result<Vec<u8>> = Err(anyhow!(\"failed to load plugin from disk\"));\n        for path in paths {\n            // Check if the plugin path matches an entry in the asset map. If so, load it directly\n            // from memory, don't bother with the disk.\n            #[cfg(not(target_family = \"wasm\"))]\n            if !cfg!(feature = \"disable_automatic_asset_installation\") && self.is_builtin() {\n                let asset_path = PathBuf::from(\"plugins\").join(&path);\n                if let Some(bytes) = ASSET_MAP.get(&asset_path) {\n                    log::debug!(\"Loaded plugin '{}' from internal assets\", path.display());\n\n                    if plugin_dir.join(&path).with_extension(\"wasm\").exists() {\n                        log::info!(\n                            \"Plugin '{}' exists in the 'PLUGIN DIR' at '{}' but is being ignored\",\n                            path.display(),\n                            plugin_dir.display()\n                        );\n                    }\n\n                    return Ok(bytes.to_vec());","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/zellij-org/zellij/blob/5cb5df5cce4e16c5d1c57c7c062e4a90a7823c41/zellij-utils/src/input/plugins.rs#L111-L147","documentation":"Seed error of PluginConfig::resolve_wasm_bytes (zellij-utils/src/input/plugins.rs): the lookup tries, in order, the raw path, path + '.wasm', plugin_dir/path.wasm, and the system data dir's plugins/path.wasm (plus the builtin ASSET_MAP for 'zellij:' plugins unless built with disable_automatic_asset_installation). This message is the initial value of last_err; the surfaced error chains each failed path's io::Error as context, so it means NO candidate location yielded readable wasm bytes.","triggerScenarios":"A `run \"name\"` / plugin entry whose wasm exists nowhere: typo'd builtin name (so the ASSET_MAP lookup misses and disk fallbacks fail), custom plugin path not relative to the plugin dir nor absolute-and-existing, unreadable file (permissions), or a distro build with disable_automatic_asset_installation where builtin wasm was never dumped to disk (then you get BuiltinPluginMissing wrapping this).","commonSituations":"Config/layout referencing 'tab-bar' as 'tabbar'; custom plugin placed in the wrong directory; plugins not installed via `zellij setup --dump-plugins` on feature-restricted builds; upgrading zellij renamed/moved a builtin plugin.","solutions":["Check the chained per-path io errors in the full error message — they list exactly which locations were tried; make your wasm exist at one of them (usually the plugin dir, e.g. ~/.local/share/zellij/plugins/).","Fix the plugin name/path in your layout or config: builtins must match exactly (e.g. \"zellij:tab-bar\"), custom plugins should be the file stem or path relative to the plugin dir.","On builds without automatic asset installation, run `zellij setup --dump-plugins` to materialize builtin wasm into the plugin dir.","Verify file permissions on the wasm and that it is a real wasm module, not an HTML error page or empty file."],"exampleFix":"// before: name matches no builtin and no file\nlayout {\n    pane {\n        plugin \"tabbar\" --flag\n    }\n}\n\n// after: exact builtin name (loaded from ASSET_MAP)\nlayout {\n    pane {\n        plugin \"zellij:tab-bar\" --flag\n    }\n}","handlingStrategy":"validation","validationCode":"// pre-check every location resolve_wasm_bytes will try\nuse zellij_utils::home::system_data_dir;\n\nfn plugin_resolvable(path: &Path, plugin_dir: &Path) -> bool {\n    let candidates = [\n        path.to_path_buf(),\n        path.with_extension(\"wasm\"),\n        plugin_dir.join(path).with_extension(\"wasm\"),\n        system_data_dir().join(\"plugins\").join(path).with_extension(\"wasm\"),\n    ];\n    candidates.iter().any(|p| p.is_file())\n}","typeGuard":null,"tryCatchPattern":"match plugin.resolve_wasm_bytes(&plugin_dir) {\n    Ok(bytes) => bytes,\n    Err(e) => {\n        // the context chain lists each attempted path — surface it to the user\n        anyhow::bail!(\"plugin '{}' could not be loaded. Checked: {e:#}\", plugin.path.display());\n    },\n}","preventionTips":["Use exact builtin names (\"zellij:tab-bar\", \"zellij:status-bar\") in layouts/configs.","Place custom wasm under the plugin dir (default ~/.local/share/zellij/plugins/) and reference it by file stem or relative path.","On disable_automatic_asset_installation builds, run `zellij setup --dump-plugins` during packaging.","Smoke-test configs in CI with `zellij setup --check` so missing plugins fail before runtime."],"tags":["zellij","rust","plugin","wasm","config","file-not-found"],"backgroundTag":"plugin-not-found","analyzedSha":"5cb5df5cce4e16c5d1c57c7c062e4a90a7823c41","analyzedAt":"2026-08-19T07:42:58.758Z","contentChangedAt":"2026-08-19T07:42:58.758Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}