{"record":{"id":"12b2433d808c6e3e","repo":"sxyazi/yazi","slug":"failed-to-join-new-name-with-parent-directory","errorCode":null,"errorMessage":"Failed to join new name with parent directory","messagePattern":"Failed to join new name with parent directory","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"yazi-actor/src/mgr/rename.rs","lineNumber":55,"sourceCode":"\t\t\t\t.position(|c| c == '.')\n\t\t\t\t.filter(|_| hovered.is_file())\n\t\t\t\t.map(|i| name.chars().count() - i - 1)\n\t\t\t\t.filter(|&i| i != 0),\n\t\t\t_ => None,\n\t\t};\n\n\t\tlet (tab, old) = (cx.tab().id, hovered.url_owned());\n\t\tlet mut input =\n\t\t\tinput!(cx, YAZI.input.rename(hovered.is_dir()).with_value(name).with_cursor(cursor))?;\n\n\t\ttokio::spawn(async move {\n\t\t\tlet Some(InputEvent::Submit(name)) = input.recv().await else { return Ok(()) };\n\t\t\tif name.is_empty() {\n\t\t\t\treturn Ok(());\n\t\t\t}\n\n\t\t\tlet Some(Ok(new)) = old.parent().map(|u| u.try_join(name)) else {\n\t\t\t\tbail!(\"Failed to join new name with parent directory\");\n\t\t\t};\n\n\t\t\tif form.force || Self::try_ask(&old, &new).await? {\n\t\t\t\tSelf::r#do(tab, old, new).await?;\n\t\t\t}\n\t\t\tOk::<(), anyhow::Error>(())\n\t\t});\n\t\tsucc!();\n\t}\n}\n\nimpl Rename {\n\tasync fn r#do(tab: Id, old: UrlBuf, new: UrlBuf) -> Result<()> {\n\t\tlet Some((old_t, old_k)) = old.pair() else { return Ok(()) };\n\t\tlet Some(_) = new.pair() else { return Ok(()) };\n\t\tlet _permit = WATCHER.acquire().await.unwrap();\n\n\t\tlet overwritten = engine::casefold(&new).await;","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/sxyazi/yazi/blob/441b332de8b8800e70784cfbfb75369ab51ed9dd/yazi-actor/src/mgr/rename.rs#L37-L73","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"-- before\nlocal f = ya.emit(\"cmd\", { entry = url })\n-- after\nya.emit(\"cmd\", { entry = hovered_file })","handlingStrategy":"type-guard","validationCode":"// Rust: probe before converting\nif a.any::<File>(\"entry\").is_some() { /* safe to get::<File> */ }","typeGuard":"// Rust\nfn as_file(data: &Data) -> Option<File> { data.as_any::<File>().cloned() }","tryCatchPattern":"// Rust\nlet file = a.get::<File>(\"entry\").map_err(|e| tracing::warn!(\"bad entry arg: {e:#}\"));","preventionTips":["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"],"tags":["rust","lua","type-conversion","plugin-api","yazi"],"backgroundTag":"wrong-type-argument","analyzedSha":"441b332de8b8800e70784cfbfb75369ab51ed9dd","analyzedAt":"2026-08-19T05:27:26.468Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}