sxyazi/yazi · error · io::Error

Not a SFTP URL: {url:?}

Error message

Not a SFTP URL: {url:?}

What it means

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.

Source

Thrown at yazi-vfs/src/engine/sftp/sftp.rs:152

	}

	async fn hard_link<P>(&self, to: P) -> io::Result<()>
	where
		P: DynPath,
	{
		let to = to.dyn_path().as_unix()?;

		Ok(self.op().await?.hardlink(self.path, to).await?)
	}

	async fn metadata(&self) -> io::Result<yazi_fs::cha::Cha> {
		let attrs = self.op().await?.stat(self.path).await?;
		Ok(Cha::try_from((self.path.file_name().unwrap_or_default(), &attrs))?.0)
	}

	async fn new<'b>(url: Url<'b>) -> io::Result<Self::Me<'b>> {
		let Url::Sftp { loc, auth } = url else {
			return Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Not a SFTP URL: {url:?}")));
		};

		let config = Vfs::service::<&ServiceSftp>(auth)?;
		Ok(Self::Me { url, path: loc.as_inner(), config })
	}

	async fn read_dir(self) -> io::Result<Self::ReadDir> {
		Ok(Self::ReadDir {
			dir:    Arc::new(self.url.to_owned()),
			reader: self.op().await?.read_dir(self.path).await?,
		})
	}

	async fn read_link(&self) -> io::Result<PathBufDyn> {
		Ok(self.op().await?.readlink(self.path).await?.into())
	}

	async fn remove_dir(&self) -> io::Result<()> { Ok(self.op().await?.rmdir(self.path).await?) }

View on GitHub (pinned to 5f901b886b)

Solutions

  1. Verify the URL kind before calling: it must be `Url::Sftp`; add an assertion/guard on `url.kind()`/variant
  2. 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
  3. Fix code that reconstructs the URL (e.g. joining with a local path) and preserve the original Sftp URL
  4. Confirm the remote entry was mounted/created via SFTP APIs, not copied from a local listing

Example fix

// before
let file = SftpFile::new(some_local_url)?; // panics into InvalidInput
// after
if let Url::Sftp { .. } = &url {
    let file = SftpFile::new(url)?;
} else {
    return engine::open(url).await; // generic dispatch
}
Defensive patterns

Strategy: type-guard

Validate before calling

if url:kind() ~= 'sftp' then error('expected SFTP URL, got '..tostring(url:kind())) end

Type guard

fn is_sftp_url(url: &Url) -> bool {
    matches!(url, Url::Sftp { .. })
}

Try / catch

match SftpFile::new(url).await {
    Err(e) if e.to_string().starts_with("Not a SFTP URL") => {
        return vfs::File::open(url).await; // generic dispatch by kind
    }
    r => r,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02). Data as JSON: /api/errors/8e5a99a46fdece85. Report an issue: GitHub.