EpicGames/lore · error · tokio::io::Error (Unsupported)

Unable to rename, file/directory mismatch

Error message

Unable to rename, file/directory mismatch

What it means

`unify_name_case_rename` fixes a path's on-disk casing by renaming it. Before renaming it compares the dir/file kind of the source and destination metadata; if one is a directory and the other is not (or vice versa), it refuses with `ErrorKind::Unsupported`, because renaming between kinds would silently move different objects. This is a safety check against merging a file and a directory that share the same case-folded name.

Solutions

  1. Inspect both paths' metadata; resolve the file/dir collision manually (delete or move one of the entries).
  2. Recompute the rename plan after the collision is resolved.
  3. Catch `ErrorKind::Unsupported` and skip/queue the entry for manual resolution instead of retrying.

Example fix

// before
unify_name_case_rename(from, to).await?;

// after
let (f, t) = (metadata(from).await?, metadata(to).await?);
if f.is_dir() != t.is_dir() {
    eprintln!("kind mismatch between {from:?} and {to:?}; resolve manually");
} else {
    unify_name_case_rename(from, to).await?;
}
Defensive patterns

Strategy: validation

Validate before calling

let from_meta = tokio::fs::metadata(&from_path).await?;
let to_meta = tokio::fs::metadata(&to_path).await?;
if from_meta.is_dir() != to_meta.is_dir() {
    // resolve the file/dir collision before renaming
}

Try / catch

match unify_name_case_rename(&from, &to).await {
    Err(e) if e.kind() == io::ErrorKind::Unsupported => {
        eprintln!("kind mismatch {from:?} <-> {to:?}; needs manual fix");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `unify_name_case_rename` where the source path and target path resolve to entries of different kinds — e.g. source is a file but the target name collides with an existing directory (or the driver's metadata for either path points at a different object than expected on a case-insensitive filesystem).

Common situations: Case-insensitive filesystems (macOS/Windows default) where 'Foo.txt' and 'foo.txt' are the same entry, but one path is a dir and the other a file; stale plans generated before the entry's type changed.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13). Data as JSON: /api/errors/2949f7a700ddfed4. Report an issue: GitHub.

Appendix: source

Thrown at lore-revision/src/util/fs.rs:830

    to_path: &'a Path,
) -> Pin<Box<dyn Future<Output = std::io::Result<()>> + Send + 'a>> {
    Box::pin(async move {
        let driver = lore_io::IoDriver::global();
        lore_debug!(
            "Try rename {} -> {}",
            from_path.display(),
            to_path.display()
        );
        if driver.rename(from_path, to_path).await.is_ok() {
            lore_debug!("Renamed {} -> {}", from_path.display(), to_path.display());
            return Ok(());
        }

        let from_metadata = driver.metadata(from_path).await?;
        let to_metadata = driver.metadata(to_path).await?;

        if from_metadata.is_dir() != to_metadata.is_dir() {
            return Err(tokio::io::Error::new(
                std::io::ErrorKind::Unsupported,
                "Unable to rename, file/directory mismatch",
            ));
        }

        if from_metadata.is_file() {
            lore_debug!(
                "Failed rename {} -> {}, replacing",
                from_path.display(),
                to_path.display()
            );
            driver.remove_file(to_path).await?;
            if let Err(err) = driver.rename(from_path, to_path).await {
                lore_debug!(
                    "Failed rename {} -> {}, try copy and delete: {err}",
                    from_path.display(),
                    to_path.display(),
                );

View on GitHub (pinned to 074eb0b0d1)