sxyazi/yazi · error

Failed to join new name with parent directory

Error message

Failed to join new name with parent directory

What it means

During interactive rename, after the user submits a new name, yazi builds the new URL by joining the submitted name onto the old file's parent via `try_join`. If the old URL has no parent (root-level entry) or the join fails (invalid name for the scheme, e.g. containing forbidden separators), the rename is aborted with this bail.

Solutions

  1. Submit a plain filename without path separators in the rename prompt
  2. Move the file out of the scheme root into a real directory before renaming
  3. In custom code, check `old.parent()` and validate `name` (no `/`, non-empty) before calling `mgr:rename`

Example fix

// before
act!(mgr:rename, cx, (old_url, name_with_slash))?  // bails
// after
let name = name_with_slash.replace('/', "_");
act!(mgr:rename, cx, (old_url, name))?
Defensive patterns

Strategy: validation

Validate before calling

fn valid_rename_target(old: &Url, name: &str) -> bool {
    !name.is_empty()
        && !name.contains('/')
        && !name.contains('\\')
        && old.parent().is_some()
}

Type guard

fn renameable(old: &Url, name: &str) -> bool {
    valid_rename_target(old, name)
}

Try / catch

match mgr_rename(cx, old, name).await {
    Err(e) if e.to_string().contains("Failed to join new name") => {
        notify::warn("Name is invalid for this location (no separators allowed)");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Submitting a non-empty name in the rename input where `old.parent()` is None (file at scheme root) or `try_join(name)` returns Err because the name contains path separators, NUL bytes, or is otherwise invalid for the URL scheme.

Common situations: User renames a file located at `/` or at the root of a remote/scheme mount; user pastes a name containing `/` or `\` into the rename prompt; a plugin drives rename programmatically with an entry whose URL is a scheme root.

Related errors


AI-assisted analysis of sxyazi/yazi@a541adde4e (2026-09-02). Data as JSON: /api/errors/12b2433d808c6e3e. Report an issue: GitHub.

Appendix: source

Thrown at yazi-actor/src/mgr/rename.rs:55

				.position(|c| c == '.')
				.filter(|_| hovered.is_file())
				.map(|i| name.chars().count() - i - 1)
				.filter(|&i| i != 0),
			_ => None,
		};

		let (tab, old) = (cx.tab().id, hovered.url_owned());
		let mut input =
			input!(cx, YAZI.input.rename(hovered.is_dir()).with_value(name).with_cursor(cursor))?;

		tokio::spawn(async move {
			let Some(InputEvent::Submit(name)) = input.recv().await else { return Ok(()) };
			if name.is_empty() {
				return Ok(());
			}

			let Some(Ok(new)) = old.parent().map(|u| u.try_join(name)) else {
				bail!("Failed to join new name with parent directory");
			};

			if form.force || Self::try_ask(&old, &new).await? {
				Self::r#do(tab, old, new).await?;
			}
			Ok::<(), anyhow::Error>(())
		});
		succ!();
	}
}

impl Rename {
	async fn r#do(tab: Id, old: UrlBuf, new: UrlBuf) -> Result<()> {
		let Some((old_t, old_k)) = old.pair() else { return Ok(()) };
		let Some(_) = new.pair() else { return Ok(()) };
		let _permit = WATCHER.acquire().await.unwrap();

		let overwritten = engine::casefold(&new).await;

View on GitHub (pinned to a541adde4e)