sxyazi/yazi · warning · anyhow::Error

Destination already exists

Error message

Destination already exists

What it means

Before each rename, bulk rename checks maybe_exists(&new) and allows it only if old and new resolve to the same inode (engine::must_identical). Otherwise the pair is refused with "Destination already exists" — a deliberate collision guard so bulk renames never clobber existing files. The pair is listed in the failed output; the batch continues.

Source

Thrown at yazi-actor/src/mgr/bulk_rename.rs:126

			return Ok(());
		}

		if !Self::ask_continue(&todo, decision)? {
			return Ok(());
		}

		let permit = WATCHER.acquire().await.unwrap();
		let (mut failed, mut succeeded) = (Vec::new(), HashMap::with_capacity(todo.len()));
		for (o, n) in todo {
			let (Ok(old), Ok(new)) =
				(Self::replace_url(&selected[o.0], root, &o), Self::replace_url(&selected[n.0], root, &n))
			else {
				failed.push((o, n, anyhow!("Invalid new or old file name")));
				continue;
			};

			if maybe_exists(&new).await && !engine::must_identical(&old, &new).await {
				failed.push((o, n, anyhow!("Destination already exists")));
			} else if let Err(e) = engine::rename(&old, &new).await {
				failed.push((o, n, e.into()));
			} else if let Ok(f) = engine::file(new).await {
				succeeded.insert(old, f);
			} else {
				failed.push((o, n, anyhow!("Failed to retrieve file info")));
			}
		}

		if !succeeded.is_empty() {
			let it = succeeded.iter().map(|(o, n)| (o.as_url(), n.url.as_url()));
			log_if_err!(Pubsub::pub_after_bulk_rename(it));
			FilesOp::rename(succeeded);
		}
		drop(permit);

		if !failed.is_empty() {
			Self::output_failed(failed).await?;

View on GitHub (pinned to 94abcfa92f)

Solutions

  1. Route the collision through a temporary unique name (A->tmp, B->A, tmp->B) across two runs
  2. Delete or move the colliding existing file, then re-run the batch
  3. On case-insensitive filesystems, split a case-only rename into two steps

Example fix

// before (single batch, banner.jpg already exists)
photo.jpg -> banner.jpg

// after (two steps)
banner.jpg -> banner.old.jpg
photo.jpg -> banner.jpg
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight the whole batch for collisions:
for (o, n) in &todo {
    let (Ok(old), Ok(new)) = (replace(o), replace(n)) else { continue };
    if maybe_exists(&new).await && !engine::must_identical(&old, &new).await {
        return Err(anyhow!("destination exists: {}", new));
    }
}

Prevention

When it happens

Trigger: The edited target name matches an existing file in the same directory, including a name produced by another pair in the same batch (swaps ordered badly), or the destination exists on a case-insensitive filesystem for a case-only rename.

Common situations: Swapping two filenames in one bulk rename (A->B while B->A); renaming to a name differing only in case on macOS/Windows; leftovers from a previous bulk rename occupying target names.

Related errors


AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16). Data as JSON: /api/errors/38d8cd297064b543. Report an issue: GitHub.