sxyazi/yazi · critical
entry name is a valid component of the SFTP URL
Error message
entry name is a valid component of the SFTP URL
What it means
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.
Source
Thrown at yazi-vfs/src/engine/sftp/read_dir.rs:45
impl FileHolder for DirEntry {
async fn file(&self) -> io::Result<File> {
let cha = self.metadata().await?;
Ok(File::from_follow(self.url(), cha).await)
}
async fn file_type(&self) -> io::Result<yazi_fs::cha::ChaType> {
Ok(ChaMode::try_from(self.dent.attrs())?.0.into())
}
async fn metadata(&self) -> io::Result<yazi_fs::cha::Cha> { Ok(Cha::try_from(&self.dent)?.0) }
fn name(&self) -> StrandCow<'_> { self.dent.name().into() }
fn path(&self) -> PathBufDyn { self.dent.path().into() }
fn url(&self) -> UrlBuf {
self.dir.try_join(self.dent.name()).expect("entry name is a valid component of the SFTP URL")
}
}
View on GitHub (pinned to 94abcfa92f)
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
Example fix
// before: hand-built ReadDir with a non-Sftp dir
let rd = ReadDir { dir: Arc::new(UrlBuf::from("/local/dir")), reader }; // url() will panic
// after: only iterate directories handed out by the engine
let rd = engine.read_dir(&sftp_dir).await?; // dir is the original UrlBuf::Sftp Defensive patterns
Strategy: type-guard
Type guard
use yazi_shared::url::{Url, UrlBuf, UrlLike};
fn is_sftp_dir(url: &UrlBuf) -> bool {
matches!(url.as_url(), Url::Sftp { .. }) // only Sftp dirs may feed the sftp ReadDir
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cache stamp does not match target
- Remote file has changed since last download
- failed to get current working directory
- Failed to get config directory
- Failed to get cache directory
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/9a54d3e88f9c1f0d.
Report an issue: GitHub.