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
- Submit a plain filename without path separators in the rename prompt
- Move the file out of the scheme root into a real directory before renaming
- 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
- Sanitize user-submitted names: strip '/' and '\\' before rename
- Skip rename for entries whose URL parent() is None
- Validate names at the input-prompt submit handler
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
- Cannot create file at root
- Target URL must be absolute
- Destination already exists
- InvalidData
- atime not available
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)