jdx/mise · error

not a directory

Error message

not a directory: {}

What it means

`build_cached_tool_layer` fingerprints a tool directory and reuses a cached layer blob when possible. Before anything else it requires the source directory to exist; a missing or non-directory path fails with `not a directory: <path>`. The cache itself also rebuilds records when blobs or diff_ids are corrupt.

Solutions

  1. Ensure the tool is installed and `src_dir.is_dir()` before invoking the cached layer build.
  2. Reinstall the tool (`mise install <tool>@<version>`) to recreate the directory.
  3. Fix path resolution (install prefix / cache_dir) if it points at the wrong location; note corrupt cache blobs are rebuilt automatically, but the source dir must still exist.

Example fix

// before
let (blob, cached) = build_cached_tool_layer(&install_dir, "opt", owner, &reloc, &cache_dir)?;

// after
if !install_dir.is_dir() {
    bail!("tool install dir missing: {} — run `mise install` first", install_dir.display());
}
let (blob, cached) = build_cached_tool_layer(&install_dir, "opt", owner, &reloc, &cache_dir)?;
Defensive patterns

Strategy: validation

Validate before calling

if !install_dir.is_dir() {
    bail!("install dir {} missing; run `mise install {}@{}` first", install_dir.display(), tool, version);
}

Try / catch

match build_cached_tool_layer(&install_dir, prefix, owner, &reloc, &cache_dir) {
    Err(e) if e.to_string().starts_with("not a directory:") => {
        bail!("tool dir {} vanished; reinstall and avoid concurrent uninstalls", install_dir.display())
    }
    r => r,
}

Prevention

When it happens

Trigger: Calling `build_cached_tool_layer` with a tool install directory that is absent or a file — e.g. the cached-build path resolved from an install dir deleted between resolution and layer packaging.

Common situations: Concurrent builds racing with a `mise uninstall`/cache clean; wrong cache_dir or install prefix; first build after switching MISE_DATA_DIR where nothing is installed yet.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/18976e93b325af8b. Report an issue: GitHub.

Appendix: source

Thrown at src/oci/layer/cache.rs:25

use super::*;

#[derive(Serialize, Deserialize)]
struct CachedLayer {
    digest: String,
    diff_id: String,
    size: u64,
}

pub(crate) fn build_cached_tool_layer(
    src_dir: &Path,
    target_prefix: &str,
    owner: LayerOwner,
    relocation: &ToolRelocation,
    cache_dir: &Path,
) -> Result<(LayerBlob, bool)> {
    if !src_dir.is_dir() {
        eyre::bail!("not a directory: {}", src_dir.display());
    }
    let entries = collect_sorted_entries(src_dir, false, owner, Some(relocation))?;
    let key = fingerprint(&entries, target_prefix, owner, relocation)?;
    let record_path = cache_dir.join(format!("{key}.json"));
    let _lock = match crate::lock_file::LockFile::new(&record_path).lock() {
        Ok(lock) => lock,
        Err(err) => {
            debug!("could not lock OCI tool layer cache: {err:#}");
            return Ok((
                build_layer_from_entries(&entries, target_prefix, owner, Some(relocation))?,
                false,
            ));
        }
    };
    match read_cached_layer(&record_path, cache_dir) {
        Ok(Some(blob)) => return Ok((blob, true)),
        Ok(None) => {}
        Err(err) => debug!("ignoring invalid OCI tool layer cache: {err:#}"),

View on GitHub (pinned to 533346cc37)