{"record":{"id":"254af54cc0a8a8a8","repo":"sxyazi/yazi","slug":"target-url-must-be-absolute","errorCode":null,"errorMessage":"Target URL must be absolute","messagePattern":"Target URL must be absolute","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"yazi-actor/src/mgr/displace_do.rs","lineNumber":28,"sourceCode":"pub struct DisplaceDo;\n\nimpl Actor for DisplaceDo {\n\ttype Form = DisplaceDoForm;\n\n\tconst NAME: &str = \"displace_do\";\n\n\tfn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {\n\t\tif cx.cwd() != opt.from {\n\t\t\tsucc!()\n\t\t}\n\n\t\tlet to = match opt.to {\n\t\t\tOk(url) => url,\n\t\t\tErr(e) => return act!(mgr:update_files, cx, FilesOp::IOErr(opt.from, e)),\n\t\t};\n\n\t\tif !to.is_absolute() {\n\t\t\tbail!(\"Target URL must be absolute\");\n\t\t} else if let Some(hovered) = cx.hovered()\n\t\t\t&& let Ok(url) = to.try_join(hovered.urn())\n\t\t{\n\t\t\tact!(mgr:reveal, cx, (url, CdSource::Displace))\n\t\t} else {\n\t\t\tact!(mgr:cd, cx, (to, CdSource::Displace))\n\t\t}\n\t}\n}\n","sourceCodeStart":10,"sourceCodeEnd":38,"githubUrl":"https://github.com/sxyazi/yazi/blob/5f901b886b14de1f17460b6e52e9de5d67f8aba9/yazi-actor/src/mgr/displace_do.rs#L10-L38","documentation":"The `mgr:displace` actor requires the replacement target URL to be absolute. Relative or fragmentary URLs cannot unambiguously identify a filesystem location to displace the current directory entry with, so the actor rejects them up front before any file operations run.","triggerScenarios":"Invoking the `displace` command/plugin (`mgr:displace`) with a `to` URL that fails `Url::is_absolute()` — e.g. a bare relative name like `foo.txt`, a scheme-only URL, or a path missing its leading component.","commonSituations":"User types a relative name into the displace input prompt; a custom keymap or shell integration passes a partially-constructed URL; a plugin builds the target with string concatenation instead of joining onto an absolute base.","solutions":["Make the target absolute: join the user input onto the current directory (`cwd.join(input)` or `old.parent().unwrap().try_join(name)`) before calling `mgr:displace`","Validate with `to.is_absolute()` in your plugin and show a user-facing message instead of calling the actor","If the input came from a prompt, configure it to prepend the current working directory automatically"],"exampleFix":"// before\nact!(mgr:displace, cx, (to, true))?  // to = \"foo.txt\"\n// after\nlet to = if to.is_absolute() { to } else { cx.cwd().try_join(to)? };\nact!(mgr:displace, cx, (to, true))?","handlingStrategy":"validation","validationCode":"if !to.is_absolute() {\n    anyhow::bail!(\"displace target must be absolute, got: {to}\");\n}\nact!(mgr:displace, cx, (to, true))?;","typeGuard":"fn is_absolute_target(url: &Url) -> bool {\n    url.is_absolute()\n}","tryCatchPattern":"match mgr_displace(cx, to).await {\n    Err(e) if e.to_string().contains(\"Target URL must be absolute\") => {\n        // retry with cwd-joined URL\n        let abs = cx.cwd().join(to);\n        mgr_displace(cx, abs).await?;\n    }\n    other => other?,\n}","preventionTips":["Resolve user input against cwd before passing to displace","Add an is_absolute() assertion in plugin wrappers","Avoid string-concatenating URLs; use try_join on an absolute base"],"tags":["filesystem","path-validation","yazi"],"backgroundTag":"relative-path-not-allowed","analyzedSha":"5f901b886b14de1f17460b6e52e9de5d67f8aba9","analyzedAt":"2026-09-02T18:38:25.566Z","contentChangedAt":"2026-09-02T18:38:25.566Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}