sxyazi/yazi · error · io::Error

Not a local URL: {url}

Error message

Not a local URL: {url}

What it means

LocalFs::new constructs a local filesystem entry from a URL, but only if `url.as_local()` yields a path. Non-local (e.g. SFTP) URLs are rejected with an InvalidInput error. It is the constructor-level equivalent of the open-time check in the local engine.

Source

Thrown at yazi-fs/src/engine/local/local.rs:75

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

		tokio::fs::hard_link(self.path, to).await
	}

	#[inline]
	async fn metadata(&self) -> io::Result<Cha> {
		Ok(Cha::new(self.path.file_name().unwrap_or_default(), tokio::fs::metadata(self.path).await?))
	}

	#[inline]
	async fn new<'b>(url: Url<'b>) -> io::Result<Self::Me<'b>> {
		let path = url.as_local().ok_or_else(|| {
			io::Error::new(io::ErrorKind::InvalidInput, format!("Not a local URL: {url}"))
		})?;

		Ok(Self::Me { url, path })
	}

	#[inline]
	async fn read_dir(self) -> io::Result<Self::ReadDir> {
		Ok(super::ReadDir(tokio::fs::read_dir(self.path).await?))
	}

	#[inline]
	async fn read_link(&self) -> io::Result<PathBufDyn> {
		Ok(tokio::fs::read_link(self.path).await?.into())
	}

	#[inline]
	async fn remove_dir(&self) -> io::Result<()> { tokio::fs::remove_dir(self.path).await }

View on GitHub (pinned to 8ebf930f17)

Solutions

  1. Dispatch on the URL scheme first: only build LocalFs entries for local URLs; use Vfs/Sftp for remote ones.
  2. If the URL should be local, strip the remote auth / construct it with `Url::from` on a real filesystem path.
  3. Add an `url.as_local().is_some()` guard before calling the constructor.

Example fix

// before
let file = LocalFs::File::new(url).await?;
// after
ensure!(url.as_local().is_some(), "use Vfs for remote URLs");
let file = LocalFs::File::new(url).await?;
Defensive patterns

Strategy: type-guard

Validate before calling

if url.as_local().is_none() { bail!("LocalFs::new requires a local URL"); }

Type guard

fn is_local(url: &Url) -> bool { url.as_local().is_some() }

Try / catch

// io::Error with InvalidInput kind
match LocalFs::File::new(url).await {
    Err(e) if e.kind() == io::ErrorKind::InvalidInput => return Vfs::entry(url).await,
    other => other.map_err(Into::into),
}

Prevention

When it happens

Trigger: Creating a LocalFs entry (File/Folder via `new`) with a remote or otherwise non-local Url, such as a Unix URL carrying SFTP auth.

Common situations: Generic code that dispatches on URL kind but falls through to LocalFs for unknown schemes; copied code that assumed all URLs in a tab are local; hover/preview logic given a remote entry.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of sxyazi/yazi@8ebf930f17 (2026-09-09). Data as JSON: /api/errors/8130c8622eb2dc14. Report an issue: GitHub.