sxyazi/yazi · error · io::Error

Not a local URL: {url:?}

Error message

Not a local URL: {url:?}

What it means

The local filesystem engine's `open()` (yazi-fs `Opener` impl in demand.rs) only opens URLs that resolve to a local path. It calls `url.as_url().as_local()`, and when that returns None — i.e. the URL is a `Mount`, `Hub`, `Scope`, or `Sftp` URL rather than a `Regular`/`Search` one — it fails with `io::ErrorKind::InvalidInput`. This is a type-dispatch guard: the local engine refuses URLs that belong to remote/virtual backends.

Source

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

	fn create(&mut self, create: bool) -> &mut Self {
		self.0.create(create);
		self
	}

	fn create_new(&mut self, create_new: bool) -> &mut Self {
		self.0.create_new(create_new);
		self
	}

	async fn open<U>(&self, url: U) -> io::Result<Self::File>
	where
		U: AsUrl,
	{
		let url = url.as_url();
		if let Some(path) = url.as_local() {
			self.0.open(path).await
		} else {
			Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Not a local URL: {url:?}")))
		}
	}

	fn read(&mut self, read: bool) -> &mut Self {
		self.0.read(read);
		self
	}

	fn truncate(&mut self, truncate: bool) -> &mut Self {
		self.0.truncate(truncate);
		self
	}

	fn write(&mut self, write: bool) -> &mut Self {
		self.0.write(write);
		self
	}
}

View on GitHub (pinned to 94abcfa92f)

Solutions

  1. Convert the URL to a local path before opening: only call the local engine when `url.as_local()` is `Some(path)`.
  2. Dispatch by URL kind: route `Sftp`/`Mount`/`Hub`/`Scope` URLs to the engine/backend that owns that kind.
  3. If you actually want the file body of an archived/remote item, use the backend-specific fetch/read API for that URL kind instead of the local opener.

Example fix

// before
let file = engine.open(url).await?; // panics path: InvalidInput for sftp:// URLs

// after
if let Some(path) = url.as_url().as_local() {
    let file = engine.open(path).await?;
} else {
    // route to the remote/mount backend for this URL kind
}
Defensive patterns

Strategy: type-guard

Validate before calling

use yazi_shared::url::Url;

fn local_path_of<U: AsUrl>(url: U) -> Option<std::path::PathBuf> {
    url.as_url().as_local().map(|p| p.to_path_buf())
}

// before opening:
let Some(path) = local_path_of(&url) else {
    // route to the engine for Sftp/Mount/Hub/Scope
    return Ok(());
};

Type guard

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

Try / catch

match engine.open(url).await {
    Ok(f) => { /* ... */ }
    Err(e) if e.kind() == io::ErrorKind::InvalidInput => {
        // URL was not local; dispatch to the proper backend
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `open(url)` (or any higher-level API that routes through the local engine, e.g. previewing or reading a file) with a URL string like `sftp://host/file`, an archive-mounted URL (`mount://...`), a hub URL, or a scope URL. Any `AsUrl` value whose `as_url().as_local()` is None triggers it.

Common situations: Passing a full URL where a plain filesystem path is expected; plugins constructing URLs from hovered entries inside archives or remote mounts and handing them to file APIs backed by the local engine; hardcoding `search://` or `hub://` strings in scripts.

Related errors


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