{"record":{"id":"8e5a99a46fdece85","repo":"sxyazi/yazi","slug":"not-a-sftp-url-url","errorCode":null,"errorMessage":"Not a SFTP URL: {url:?}","messagePattern":"Not a SFTP URL: (.+?)","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"yazi-vfs/src/engine/sftp/sftp.rs","lineNumber":152,"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::Sftp { 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 = Vfs::service::<&ServiceSftp>(auth)?;\n\t\tOk(Self::Me { url, path: loc.as_inner(), config })\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\n\tasync fn remove_dir(&self) -> io::Result<()> { Ok(self.op().await?.rmdir(self.path).await?) }","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/sxyazi/yazi/blob/5f901b886b14de1f17460b6e52e9de5d67f8aba9/yazi-vfs/src/engine/sftp/sftp.rs#L134-L170","documentation":"The SFTP engine's constructor requires a `Url::Sftp { loc, auth }` variant; any other URL is rejected with InvalidInput and a message naming the actual variant received. It's a routing/type error: the SFTP engine was asked to operate on a location that isn't owned by the SFTP backend.","triggerScenarios":"Calling SFTP-backed file operations with a local, Mount/Hub/Scope (custom Lua VFS), Archive, or Search URL; or a URL that was re-parented/rewritten so it lost its Sftp variant.","commonSituations":"Plugin or config code constructing URLs manually instead of through the VFS, tab previews mixing local and remote entries, or upgrades that changed URL variant names leaving stale constructed URLs.","solutions":["Verify the URL kind before calling: it must be `Url::Sftp`; add an assertion/guard on `url.kind()`/variant","Route the operation through the generic VFS dispatcher (`yazi-vfs`) so the correct engine is selected by URL kind instead of calling the SFTP engine directly","Fix code that reconstructs the URL (e.g. joining with a local path) and preserve the original Sftp URL","Confirm the remote entry was mounted/created via SFTP APIs, not copied from a local listing"],"exampleFix":"// before\nlet file = SftpFile::new(some_local_url)?; // panics into InvalidInput\n// after\nif let Url::Sftp { .. } = &url {\n    let file = SftpFile::new(url)?;\n} else {\n    return engine::open(url).await; // generic dispatch\n}","handlingStrategy":"type-guard","validationCode":"if url:kind() ~= 'sftp' then error('expected SFTP URL, got '..tostring(url:kind())) end","typeGuard":"fn is_sftp_url(url: &Url) -> bool {\n    matches!(url, Url::Sftp { .. })\n}","tryCatchPattern":"match SftpFile::new(url).await {\n    Err(e) if e.to_string().starts_with(\"Not a SFTP URL\") => {\n        return vfs::File::open(url).await; // generic dispatch by kind\n    }\n    r => r,\n}","preventionTips":["Route remote operations through the generic VFS layer instead of calling the SFTP engine directly","Preserve the original URL when joining/rewriting paths — don't rebuild a Url from a local Path","Check url kind before engine-specific calls at API boundaries","After upgrades, verify URL variant construction still matches the Sftp shape"],"tags":["sftp","url","invalid-input"],"backgroundTag":"invalid-url-variant","analyzedSha":"5f901b886b14de1f17460b6e52e9de5d67f8aba9","analyzedAt":"2026-09-02T18:38:25.566Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}