swc-project/swc · error · anyhow::Error

Failed to resolve plugin path: {resolved_path:?}

Error message

Failed to resolve plugin path: {resolved_path:?}

What it means

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.

Source

Thrown at crates/swc/src/plugin.rs:266

    let mut inner_cache = crate::config::PLUGIN_MODULE_CACHE
        .inner
        .get()
        .expect("Cache should be available")
        .lock();

    // Populate cache to the plugin modules if not loaded
    for plugin_config in plugins.iter() {
        let plugin_name = &plugin_config.0;

        if !inner_cache.contains(plugin_runtime, plugin_name) {
            let resolved_path = plugin_resolver
                .resolve(&FileName::Real(PathBuf::from(plugin_name)), plugin_name)
                .with_context(|| format!("failed to resolve plugin path: {plugin_name}"))?;

            let path = if let FileName::Real(value) = resolved_path.filename {
                value
            } else {
                anyhow::bail!("Failed to resolve plugin path: {resolved_path:?}");
            };

            inner_cache.store_bytes_from_path(plugin_runtime, &path, plugin_name)?;
            #[cfg(debug_assertions)]
            tracing::debug!("Initialized WASM plugin {plugin_name}");
        }
    }

    Ok(())
}

View on GitHub (pinned to d7d7434666)

Solutions

  1. 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
  2. Verify the plugin package actually ships the wasm binary (ls node_modules/<plugin> and check the binary field / files) and reinstall it
  3. 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
  4. If you inject a custom resolver/loader, ensure resolve() returns FileName::Real(PathBuf) for plugin specifiers

Example fix

// .swcrc before (non-path specifier)
"jsc": { "experimental": { "plugins": [ [ "https://cdn.example.com/my_plugin.wasm", {} ] ] } }

// .swcrc after (real, resolvable path)
"jsc": { "experimental": { "plugins": [ [ "my_swc_plugin", {} ] ] } }
// where node_modules/my_swc_plugin resolves to a real .wasm file
Defensive patterns

Strategy: validation

Validate before calling

// Rust: before transform, confirm every plugin resolves to a real file
use std::path::PathBuf;
use swc_ecma_loader::resolvers::{NodeModulesResolver, Resolve};

fn plugins_resolvable(plugins: &[(&str, serde_json::Value)]) -> Result<(), String> {
    let resolver = NodeModulesResolver::new(swc_ecma_loader::TargetEnv::Node, Default::default(), true);
    for (name, _) in plugins {
        let resolved = resolver
            .resolve(&swc_common::FileName::Real(PathBuf::from(name)), name)
            .map_err(|e| format!("plugin {name}: {e:#}"))?;
        match resolved.filename {
            swc_common::FileName::Real(p) => {
                if !p.is_file() { return Err(format!("plugin {name}: not a file: {}", p.display())); }
            }
            other => return Err(format!("plugin {name}: resolved to non-real filename {other:?}")),
        }
    }
    Ok(())
}

Try / catch

// Wrap compile_wasm_plugins / transform setup and surface the plugin name
match result {
    Ok(_) => {}
    Err(err) if err.to_string().contains("Failed to resolve plugin path") => {
        eprintln!("plugin config is invalid; check jsc.experimental.plugins entries: {err:#}");
        std::process::exit(1);
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of swc-project/swc@d7d7434666 (2026-08-16). Data as JSON: /api/errors/c5bfb2567f5c4349. Report an issue: GitHub.