jdx/mise · error

remote cache directory contains duplicate names

Error message

remote cache directory contains duplicate names

What it means

Within a remote directory blob, all child entry names (across directories, files, and symlinks) must be unique. When walking the `directories` list, inserting a name that is already in the names set bails, because a filesystem cannot contain two entries with the same name.

Solutions

  1. Delete the offending directory blob from the remote store and re-run the task to regenerate it.
  2. Fix the producer to deduplicate entries by name before serializing the directory blob.
  3. Validate cached blobs with a script that checks for duplicate names across directories/files/symlinks.
  4. Avoid hand-editing remote cache blobs.

Example fix

// before: duplicate subdirectory names
{"version":1,"directories":[{"name":"a",...},{"name":"a",...}],...}
// after: unique names
{"version":1,"directories":[{"name":"a",...}],...}
Defensive patterns

Strategy: validation

Validate before calling

const names = blob.directories.map(d => d.name);
if (new Set(names).size !== names.length) throw new Error('duplicate directory names');

Prevention

When it happens

Trigger: A remote directory blob lists two subdirectory entries with the same `name` value in its `directories` array.

Common situations: Blobs produced by a buggy or malicious cache writer that doesn't deduplicate entries; hand-edited JSON; corrupted remote data after a bad merge of directory listings.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/task/task_cache_store.rs:729

    let mut pending = vec![(PathBuf::new(), root.clone(), BTreeSet::new())];
    let mut nodes = BTreeMap::<PathBuf, RestoredNode>::new();
    while let Some((path, digest, mut ancestors)) = pending.pop() {
        if !ancestors.insert(digest.clone()) {
            bail!("remote cache directory graph contains a cycle");
        }
        let bytes = store.client.get_blob(&digest, DIRECTORY_MEDIA_TYPE).await?;
        let directory: RemoteDirectory = serde_json::from_slice(&bytes)?;
        if canonical_json(&serde_json::to_value(&directory)?)? != bytes {
            bail!("remote cache directory is not canonical JSON");
        }
        if directory.version != 1 {
            bail!("unsupported remote cache directory version");
        }
        let mut names = BTreeSet::new();
        for directory in directory.directories {
            validate_cache_name(&directory.name)?;
            if !names.insert(directory.name.clone()) {
                bail!("remote cache directory contains duplicate names");
            }
            let child = path.join(&directory.name);
            validate_cache_path(&child)?;
            nodes.insert(
                child.clone(),
                RestoredNode::Directory {
                    mode: directory.mode,
                },
            );
            pending.push((child, directory.digest, ancestors.clone()));
        }
        for file in directory.files {
            validate_cache_name(&file.name)?;
            if !names.insert(file.name.clone()) {
                bail!("remote cache directory contains duplicate names");
            }
            let child = path.join(&file.name);
            validate_cache_path(&child)?;

View on GitHub (pinned to 533346cc37)