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

  1. 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
  2. If it panics inside stock yazi, reproduce with YAZI_LOG=debug, capture the dir URL and entry name, and file an upstream bug
  3. Verify all yazi-* crates resolve to the same release (cargo tree | grep yazi) and align them with cargo update if forked versions mixed
  4. 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

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


AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16). Data as JSON: /api/errors/9a54d3e88f9c1f0d. Report an issue: GitHub.