{"record":{"id":"9a54d3e88f9c1f0d","repo":"sxyazi/yazi","slug":"entry-name-is-a-valid-component-of-the-sftp-url","errorCode":null,"errorMessage":"entry name is a valid component of the SFTP URL","messagePattern":"entry name is a valid component of the SFTP URL","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"yazi-vfs/src/engine/sftp/read_dir.rs","lineNumber":45,"sourceCode":"\nimpl FileHolder for DirEntry {\n\tasync fn file(&self) -> io::Result<File> {\n\t\tlet cha = self.metadata().await?;\n\t\tOk(File::from_follow(self.url(), cha).await)\n\t}\n\n\tasync fn file_type(&self) -> io::Result<yazi_fs::cha::ChaType> {\n\t\tOk(ChaMode::try_from(self.dent.attrs())?.0.into())\n\t}\n\n\tasync fn metadata(&self) -> io::Result<yazi_fs::cha::Cha> { Ok(Cha::try_from(&self.dent)?.0) }\n\n\tfn name(&self) -> StrandCow<'_> { self.dent.name().into() }\n\n\tfn path(&self) -> PathBufDyn { self.dent.path().into() }\n\n\tfn url(&self) -> UrlBuf {\n\t\tself.dir.try_join(self.dent.name()).expect(\"entry name is a valid component of the SFTP URL\")\n\t}\n}\n","sourceCodeStart":27,"sourceCodeEnd":48,"githubUrl":"https://github.com/sxyazi/yazi/blob/94abcfa92f4ad3f0a1aef6c1ea083cfb8aa6c8c2/yazi-vfs/src/engine/sftp/read_dir.rs#L27-L48","documentation":"DirEntry::url() builds an entry's URL with dir.try_join(self.dent.name()) and unwraps JoinError with this expect. try_join fails with \"calling join on paths with different encodings\" (StrandError::AsOs / PathDynError) only when the directory URL and the entry-name strand use incompatible encodings. For a genuine Sftp URL (Unix-encoded location, raw-byte names) the join is infallible, so this panic is an internal-invariant break, not a user error.","triggerScenarios":"Calling DirEntry::url() after a ReadDir was constructed with a dir URL whose variant/encoding is not Sftp/Unix — e.g. hand-building yazi_vfs::engine::sftp::ReadDir in tests or FFI with a Regular/Os-encoded UrlBuf — so try_join hits a Strand/PathDyn conversion error.","commonSituations":"Direct construction of the sftp engine types outside the engine (tests, forks, Lua bindings); mixing yazi-shared and yazi-vfs from different releases whose URL encodings disagree; a genuine upstream bug producing a non-Sftp dir for an sftp:// mount.","solutions":["Do not construct ReadDir/DirEntry by struct literal; obtain them from the SFTP engine's read_dir so dir is always the original UrlBuf::Sftp","If it panics inside stock yazi, reproduce with YAZI_LOG=debug, capture the dir URL and entry name, and file an upstream bug","Verify all yazi-* crates resolve to the same release (cargo tree | grep yazi) and align them with cargo update if forked versions mixed","In forks, keep the invariant: only Unix-encoded URLs may reach the sftp ReadDir"],"exampleFix":"// before: hand-built ReadDir with a non-Sftp dir\nlet rd = ReadDir { dir: Arc::new(UrlBuf::from(\"/local/dir\")), reader }; // url() will panic\n\n// after: only iterate directories handed out by the engine\nlet rd = engine.read_dir(&sftp_dir).await?; // dir is the original UrlBuf::Sftp","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"use yazi_shared::url::{Url, UrlBuf, UrlLike};\n\nfn is_sftp_dir(url: &UrlBuf) -> bool {\n    matches!(url.as_url(), Url::Sftp { .. }) // only Sftp dirs may feed the sftp ReadDir\n}","tryCatchPattern":null,"preventionTips":["Obtain ReadDir only from the SFTP engine's read_dir; never construct ReadDir/DirEntry by struct literal","Keep all yazi-* crates on one release line so URL encodings stay compatible","Treat any panic from DirEntry::url() as an upstream bug: reproduce with YAZI_LOG=debug and report the dir URL plus entry name"],"tags":["sftp","panic","invariant","encoding"],"backgroundTag":null,"analyzedSha":"94abcfa92f4ad3f0a1aef6c1ea083cfb8aa6c8c2","analyzedAt":"2026-08-16T09:56:24.836Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}