sxyazi/yazi · error · anyhow::Error
Failed to join new name with parent directory
Error message
Failed to join new name with parent directory
What it means
Thrown by TryFrom<&Data> for File (yazi-fs/src/file/data.rs:21) when Data::as_any::<File>() fails on a borrowed read: the argument exists but is not a Data::Any containing a File (as_any returns None for every other variant, then .cloned() cannot produce a File). This borrowed path is what Action::get-style read accessors use, so it fires when a handler reads a File-typed argument by reference and the value has a different shape.
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 441b332de8)
Solutions
- Send a real File object in that argument slot (hovered/entry userdata), not a path string or table
- Use Files when multiple entries are involved
- Type-check on the Lua side before emitting and fail fast with a clear message
- Match plugin and yazi versions so the Any payloads have identical concrete types
Example fix
-- before
local f = ya.emit("cmd", { entry = url })
-- after
ya.emit("cmd", { entry = hovered_file }) Defensive patterns
Strategy: type-guard
Validate before calling
// Rust: probe before converting
if a.any::<File>("entry").is_some() { /* safe to get::<File> */ } Type guard
// Rust
fn as_file(data: &Data) -> Option<File> { data.as_any::<File>().cloned() } Try / catch
// Rust
let file = a.get::<File>("entry").map_err(|e| tracing::warn!("bad entry arg: {e:#}")); Prevention
- Read File arguments only from slots the sender fills with File userdata
- Use any::<File>(name) to probe without consuming or failing
- Document each action argument's expected type in plugin docs
- Keep yazi and plugin versions in lockstep so Any types match
When it happens
Trigger: A form or plugin callback calling get::<File>(name) where the argument holds a String/Integer/Table or an Any of another concrete type (Files, Folder, SpotLock, etc.); forwarding an argument slot between commands with its type changed along the way.
Common situations: Same as the owned flavor: passing URLs or plain tables instead of File userdata; argument slots reused across commands with different types after refactors; plugin API misuse after docs/examples drift from the binary version.
Related errors
- Due to Cargo's limitations, Yazi on crates.io must be built
- Some files cannot be selected, due to path nesting conflict.
- Unwinding must be enabled for Windows. Please use `cargo bui
- Due to Cargo's limitations, Yazi on crates.io must be built
- Unknown copy type: {}
AI-assisted analysis of sxyazi/yazi@441b332de8 (2026-08-19).
Data as JSON: /api/errors/12b2433d808c6e3e.
Report an issue: GitHub.