{"record":{"id":"1b1882d027577058","repo":"sxyazi/yazi","slug":"not-a-sftp-url-url-1b1882","errorCode":null,"errorMessage":"Not a SFTP URL: {url}","messagePattern":"Not a SFTP URL: (.+?)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"yazi-vfs/src/engine/sftp/sftp.rs","lineNumber":124,"sourceCode":"\t}\n\n\tasync fn hard_link<P>(&self, to: P) -> io::Result<()>\n\twhere\n\t\tP: DynPath,\n\t{\n\t\tlet to = to.dyn_path().as_unix()?;\n\n\t\tOk(self.op().await?.hardlink(self.path, to).await?)\n\t}\n\n\tasync fn metadata(&self) -> io::Result<yazi_fs::cha::Cha> {\n\t\tlet attrs = self.op().await?.stat(self.path).await?;\n\t\tOk(Cha::try_from((self.path.file_name().unwrap_or_default(), &attrs))?.0)\n\t}\n\n\tasync fn new<'b>(url: Url<'b>) -> io::Result<Self::Me<'b>> {\n\t\tlet Url::Unix { loc, auth } = url else {\n\t\t\treturn Err(io::Error::new(io::ErrorKind::InvalidInput, format!(\"Not a SFTP URL: {url}\")));\n\t\t};\n\n\t\tlet config: Arc<ServiceSftp> = Vfs::service(auth)?;\n\t\tlet pool = Conn::pool(config.clone());\n\t\tOk(Self::Me { url, path: loc.as_inner(), config, pool })\n\t}\n\n\tasync fn read_dir(self) -> io::Result<Self::ReadDir> {\n\t\tOk(Self::ReadDir {\n\t\t\tdir:    Arc::new(self.url.to_owned()),\n\t\t\treader: self.op().await?.read_dir(self.path).await?,\n\t\t})\n\t}\n\n\tasync fn read_link(&self) -> io::Result<PathBufDyn> {\n\t\tOk(self.op().await?.readlink(self.path).await?.into())\n\t}\n","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/sxyazi/yazi/blob/8ebf930f1796ae2076b7d6b0c5e93a1002c18134/yazi-vfs/src/engine/sftp/sftp.rs#L106-L142","documentation":"SftpFs::new requires the URL to be the Unix variant carrying auth (`Url::Unix { loc, auth }`), which is how SFTP locations are represented. Any other URL shape (local OS/Unix-without-auth) is rejected with \"Not a SFTP URL\". The error mirrors the local engine's guard but in reverse.","triggerScenarios":"Calling the SFTP engine's `new` with a local URL (Url::Os or Url::Unix without auth), e.g. routing every file through Vfs without checking scheme.","commonSituations":"Preview/thumbnail or task code that assumes Vfs handles all URLs but passes a purely local path; a missing `auth` component after hand-building a Unix URL; config pointing a remote path at a non-remote scheme.","solutions":["Check the URL variant is `Url::Unix` with auth before using the SFTP engine; use the local engine for local URLs.","If the target is remote, construct the URL with its SFTP auth intact (don't strip auth via `physical()`/as_local conversions).","Route by scheme: local -> yazi-fs local engine, ssh/sftp -> yazi-vfs SftpFs."],"exampleFix":"// before\nlet file = SftpFs::File::new(url).await?; // url is local\n// after\nmatch &url {\n    Url::Unix { auth, .. } if !auth.is_empty() => SftpFs::File::new(url).await?,\n    _ => LocalFs::File::new(url).await?,\n}","handlingStrategy":"type-guard","validationCode":"let is_sftp = matches!(url, Url::Unix { .. } if url.auth().map_or(false, |a| !a.is_empty()));\nif !is_sftp { bail!(\"SftpFs requires a Unix URL with auth\"); }","typeGuard":"fn as_sftp(url: &Url) -> Option<(&Loc, &AuthArc)> {\n    match url { Url::Unix { loc, auth } if !auth.is_empty() => Some((loc, auth)), _ => None }\n}","tryCatchPattern":"// io::Error with InvalidInput kind\nmatch SftpFs::File::new(url).await {\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => return LocalFs::File::new(url).await.map_err(Into::into),\n    other => other.map_err(Into::into),\n}","preventionTips":["Only pass URLs whose auth component is intact to the SFTP engine.","Avoid stripping auth via as_local()/physical() before remote operations.","Route by scheme so local URLs never reach SftpFs and vice versa."],"tags":["io","url","sftp","rust"],"backgroundTag":"invalid-url","analyzedSha":"8ebf930f1796ae2076b7d6b0c5e93a1002c18134","analyzedAt":"2026-09-09T22:26:16.379Z","contentChangedAt":"2026-09-09T22:26:16.379Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}