jdx/mise · error

not a directory: {src_dir.display}

Error message

not a directory: {src_dir.display}

What it means

build_layer_from_dir builds an OCI layer from a host directory, and first verifies that src_dir actually is a directory. This error is thrown when the given path does not exist or is a file/symlink to something that is not a directory.

Source

Thrown at src/oci/layer.rs:133

        return Err(format!("{name} must not be empty"));
    }
    value
        .parse::<u32>()
        .map_err(|_| format!("{name} must be a non-negative integer <= {}", u32::MAX))
}

/// Build a reproducible gzipped tar layer from files in `src_dir`, placing them
/// under `target_prefix` inside the tar (e.g. `/mise/installs/node/20.0.0`).
///
/// `src_dir` must exist and be a directory. Symlinks are preserved; their
/// targets are NOT followed. `owner` is applied to every emitted tar entry.
pub(crate) fn build_layer_from_dir(
    src_dir: &Path,
    target_prefix: &str,
    owner: LayerOwner,
) -> Result<LayerBlob> {
    if !src_dir.is_dir() {
        eyre::bail!("not a directory: {}", src_dir.display());
    }

    let entries = collect_sorted_entries(src_dir, false, owner, None)?;
    build_layer_from_entries(&entries, target_prefix, owner, None)
}

/// Build a tool layer while rebasing host paths embedded by its installer.
pub(crate) fn build_relocated_tool_layer_from_dir(
    src_dir: &Path,
    target_prefix: &str,
    owner: LayerOwner,
    relocation: &ToolRelocation,
) -> Result<LayerBlob> {
    if !src_dir.is_dir() {
        eyre::bail!("not a directory: {}", src_dir.display());
    }

    let entries = collect_sorted_entries(src_dir, false, owner, Some(relocation))?;

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Verify the path exists and is a directory: `ls -la <path>`; fix the path in the config or create the directory.
  2. If you intended to copy a single file, use the file-copy path (build_layer_from_path) rather than the directory variant.
  3. Check the working directory / relative path base the config is resolved against and use an absolute path if needed.

Example fix

// before
build_layer_from_dir(Path::new("assets/logo.png"), "app", owner)
// after
build_layer_from_dir(Path::new("assets"), "app", owner)
Defensive patterns

Strategy: validation

Validate before calling

fn assert_dir(p: &Path) -> Result<()> {
    if !p.is_dir() {
        return Err(eyre!("not a directory: {}", p.display()));
    }
    Ok(())
}
assert_dir(&src_dir)?;

Type guard

fn is_dir_or_fail(p: &Path) -> Option<&Path> {
    if p.is_dir() { Some(p) } else { None }
}

Try / catch

match build_layer_from_dir(&src, prefix, owner) {
    Err(e) if e.to_string().starts_with("not a directory") => {
        eprintln!("{} missing or not a dir — fix the path or use the file-copy path", src.display());
    }
    r => r,
}

Prevention

When it happens

Trigger: Calling build_layer_from_dir (directly or via build_layer_from_path) with a path that is missing, a regular file, or a dangling symlink — `src_dir.is_dir()` returns false and the bail fires before collecting entries.

Common situations: Config pointing a copy/dir layer at a file instead of a directory; a path deleted or renamed since the config was written; relative path resolved from the wrong working directory; case-sensitivity mismatch on case-sensitive filesystems.

Related errors


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