jdx/mise · error

local plugin source is not a directory: {}

Error message

local plugin source is not a directory: {}

What it means

During local plugin install, mise requires the source to be a directory (a checked-out plugin repo). If the given path exists but is a regular file (or other non-directory), validate_local_plugin_source bails with this error.

Source

Thrown at src/plugins/mod.rs:559

    match source {
        PluginSource::Git {
            url,
            git_ref: None,
            subdir: None,
        } if url == repository => path.is_absolute().then_some(path),
        _ => None,
    }
}

pub(crate) fn validate_local_plugin_source(source: &Path, plugin_path: &Path) -> Result<()> {
    if !source.exists() {
        bail!(
            "local plugin directory does not exist: {}",
            display_path(source)
        );
    }
    if !source.is_dir() {
        bail!(
            "local plugin source is not a directory: {}",
            display_path(source)
        );
    }
    let resolved_source = file::desymlink_path(source);
    let resolved_plugin_path = match (plugin_path.parent(), plugin_path.file_name()) {
        (Some(parent), Some(file_name)) => file::desymlink_path(parent).join(file_name),
        _ => plugin_path.to_path_buf(),
    };
    if resolved_source
        .ancestors()
        .any(|path| file::paths_eq(path, &resolved_plugin_path))
        || resolved_plugin_path
            .ancestors()
            .any(|path| file::paths_eq(path, &resolved_source))
    {
        bail!(
            "local plugin source cannot contain, be, or be inside the plugin install path: {}",

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Pass the plugin directory, not a file — for archives, extract first (mkdir d && tar -xf plugin.tar.gz -C d) and install from d.
  2. If pointing at a symlink, verify it resolves to a directory (ls -la <path>).
  3. Re-clone the plugin repository and install from the cloned directory.

Example fix

// before
mise plugin install my-plugin ./my-plugin-v1.0.tar.gz
// after
tar -xf my-plugin-v1.0.tar.gz && mise plugin install my-plugin ./my-plugin
Defensive patterns

Strategy: validation

Validate before calling

import std::path::Path;
fn ensure_directory(p: &Path) -> std::io::Result<()> {
    if !p.is_dir() {
        return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("{} is not a directory", p.display())));
    }
    Ok(())
}

Try / catch

match install_local_plugin(name, path) {
    Err(e) if e.to_string().contains("is not a directory") => {
        eprintln!("extract the archive and pass the extracted directory");
    }
    r => r,
}

Prevention

When it happens

Trigger: `mise plugin install <name> <path>` where <path> points at a file — e.g. a tarball/zip of the plugin, the plugin's main script, or a symlink resolving to a file — instead of the plugin's directory.

Common situations: Passing a downloaded plugin archive file instead of extracting it; pointing at the plugin's executable script rather than its repo root; a stale symlink whose target was replaced by a file.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/4ca01aea4281e963. Report an issue: GitHub.