{"record":{"id":"c5bfb2567f5c4349","repo":"swc-project/swc","slug":"failed-to-resolve-plugin-path-resolved-path","errorCode":null,"errorMessage":"Failed to resolve plugin path: {resolved_path:?}","messagePattern":"Failed to resolve plugin path: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/swc/src/plugin.rs","lineNumber":266,"sourceCode":"    let mut inner_cache = crate::config::PLUGIN_MODULE_CACHE\n        .inner\n        .get()\n        .expect(\"Cache should be available\")\n        .lock();\n\n    // Populate cache to the plugin modules if not loaded\n    for plugin_config in plugins.iter() {\n        let plugin_name = &plugin_config.0;\n\n        if !inner_cache.contains(plugin_runtime, plugin_name) {\n            let resolved_path = plugin_resolver\n                .resolve(&FileName::Real(PathBuf::from(plugin_name)), plugin_name)\n                .with_context(|| format!(\"failed to resolve plugin path: {plugin_name}\"))?;\n\n            let path = if let FileName::Real(value) = resolved_path.filename {\n                value\n            } else {\n                anyhow::bail!(\"Failed to resolve plugin path: {resolved_path:?}\");\n            };\n\n            inner_cache.store_bytes_from_path(plugin_runtime, &path, plugin_name)?;\n            #[cfg(debug_assertions)]\n            tracing::debug!(\"Initialized WASM plugin {plugin_name}\");\n        }\n    }\n\n    Ok(())\n}\n","sourceCodeStart":248,"sourceCodeEnd":277,"githubUrl":"https://github.com/swc-project/swc/blob/d7d74346665e36e692aa07e13d4ee3ee692ee35c/crates/swc/src/plugin.rs#L248-L277","documentation":"Thrown by swc's plugin loader (compile_wasm_plugins) when a plugin entry from jsc.experimental.plugins resolves successfully but the resolver returns a FileName variant other than FileName::Real. The plugin pipeline needs a real on-disk .wasm file to read bytes into the module cache (store_bytes_from_path), so a virtual, custom, or internal filename cannot be loaded. The error prints the whole resolved FileName for inspection.","triggerScenarios":"A PluginConfig whose spec resolves through NodeModulesResolver to something non-Real, e.g. a URL or virtual module produced by a custom loader chain, or a FileName::Custom/FileName::Internal leaking out of the resolver. Concretely: calling swc core transform with jsc.experimental.plugins containing a specifier that is not a plain filesystem path (relative ./pkg or node_modules package name).","commonSituations":"Typo'd plugin names that hit a fallback resolver path, plugins specified as http URLs, using swc inside a bundler that swaps in virtual file names, or a plugin package that re-exports through a redirect the resolver represents as a custom filename.","solutions":["Use a plain filesystem specifier for each plugin: a node_modules package name or a ./relative/path/pkg style path whose resolved target is a real .wasm on disk","Verify the plugin package actually ships the wasm binary (ls node_modules/<plugin> and check the binary field / files) and reinstall it","Print/inspect the {resolved_path:?} value in the error to see which FileName variant came back, then adjust the specifier so the resolver returns a real path","If you inject a custom resolver/loader, ensure resolve() returns FileName::Real(PathBuf) for plugin specifiers"],"exampleFix":"// .swcrc before (non-path specifier)\n\"jsc\": { \"experimental\": { \"plugins\": [ [ \"https://cdn.example.com/my_plugin.wasm\", {} ] ] } }\n\n// .swcrc after (real, resolvable path)\n\"jsc\": { \"experimental\": { \"plugins\": [ [ \"my_swc_plugin\", {} ] ] } }\n// where node_modules/my_swc_plugin resolves to a real .wasm file","handlingStrategy":"validation","validationCode":"// Rust: before transform, confirm every plugin resolves to a real file\nuse std::path::PathBuf;\nuse swc_ecma_loader::resolvers::{NodeModulesResolver, Resolve};\n\nfn plugins_resolvable(plugins: &[(&str, serde_json::Value)]) -> Result<(), String> {\n    let resolver = NodeModulesResolver::new(swc_ecma_loader::TargetEnv::Node, Default::default(), true);\n    for (name, _) in plugins {\n        let resolved = resolver\n            .resolve(&swc_common::FileName::Real(PathBuf::from(name)), name)\n            .map_err(|e| format!(\"plugin {name}: {e:#}\"))?;\n        match resolved.filename {\n            swc_common::FileName::Real(p) => {\n                if !p.is_file() { return Err(format!(\"plugin {name}: not a file: {}\", p.display())); }\n            }\n            other => return Err(format!(\"plugin {name}: resolved to non-real filename {other:?}\")),\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"// Wrap compile_wasm_plugins / transform setup and surface the plugin name\nmatch result {\n    Ok(_) => {}\n    Err(err) if err.to_string().contains(\"Failed to resolve plugin path\") => {\n        eprintln!(\"plugin config is invalid; check jsc.experimental.plugins entries: {err:#}\");\n        std::process::exit(1);\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Keep plugin entries as node_modules package names or plain relative paths - never URLs or virtual specifiers","Verify the plugin package's files/binaries in CI (a small script asserting the .wasm exists)","Run a smoke transform in CI so bad plugin specs fail the build, not production"],"tags":["swc","plugins","wasm","path-resolution","config"],"backgroundTag":null,"analyzedSha":"d7d74346665e36e692aa07e13d4ee3ee692ee35c","analyzedAt":"2026-08-16T12:41:13.160Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}