jdx/mise · error · eyre::Report

not a directory: {}

Error message

not a directory: {}

What it means

build_layer_from_dir requires src_dir to exist and be a directory; the guard fails fast before walking entries. Layers built from directories (tool installs, dotfile dirs) call this with the on-disk install root, so the error means the expected directory is missing or is not a directory.

Source

Thrown at src/oci/layer.rs:100

        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 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)?;
    build_layer_from_entries(&entries, target_prefix, owner)
}

/// Build a reproducible layer from one host file, symlink, or directory.
/// Directory contents are placed under `image_path`; a file or symlink is
/// placed at `image_path` itself.
pub fn build_layer_from_path(
    host_path: &Path,
    image_path: &str,
    owner: LayerOwner,
) -> Result<LayerBlob> {
    let metadata = std::fs::symlink_metadata(host_path)
        .wrap_err_with(|| format!("reading metadata for {}", host_path.display()))?;
    let target = image_path.trim_matches('/');

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Run `mise install` (or `mise use -f`) so every tool version referenced exists under the installs directory, then rebuild.
  2. Verify the source path with `ls -ld <path>`; fix the config or create the directory.
  3. If the source is intentionally a single file, point the entry at the file with an exact destination file name instead of expecting directory semantics.

Example fix

# before
mise oci build          # tools never installed

# after
mise install && mise oci build
Defensive patterns

Strategy: validation

Validate before calling

import os, sys
for d in required_source_dirs:
    if not os.path.isdir(d):
        sys.exit(f"missing source directory: {d}; run `mise install` first")

Type guard

fn is_usable_source_dir(p: &Path) -> bool {
    p.is_dir() // symlink to dir also true; files/fifos/missing are false
}

Prevention

When it happens

Trigger: A [copy]/layer source path pointing at a regular file or a nonexistent path where a directory is required; a tool's install directory missing because the tool was never installed before `mise oci build` (the OCI builder consumes already-installed versions).

Common situations: Running mise oci build on a clean machine without `mise install` first; a typo'd or moved source path in config; a symlink-to-file passed where the caller expects a directory.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/317c01ca4d771544. Report an issue: GitHub.