sxyazi/yazi · error · io::Error

Not a local URL: {:?}

Error message

Not a local URL: {:?}

What it means

`Local::read_dir` (local.rs:104) dispatches on `self.url.kind()`: `Regular` and `Search` are listed via `tokio::fs::read_dir`, but `Mount | Hub | Scope | Sftp` kinds return `InvalidInput` with `Not a local URL: {:?}`. This is reachable because a `Url::Search` can carry a location whose kind is non-local (e.g. a search rooted inside an archive mount), even though `Local::new` accepted it.

Source

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

	#[inline]
	async fn new<'b>(url: Url<'b>) -> io::Result<Self::Me<'b>> {
		match url {
			Url::Regular(loc) | Url::Search { loc, .. } => Ok(Self::Me { url, path: loc.as_inner() }),
			Url::Mount { .. } | Url::Hub { .. } | Url::Scope { .. } | Url::Sftp { .. } => {
				Err(io::Error::new(io::ErrorKind::InvalidInput, format!("Not a local URL: {url:?}")))
			}
		}
	}

	#[inline]
	async fn read_dir(self) -> io::Result<Self::ReadDir> {
		Ok(match self.url.kind() {
			AuthKind::Regular => Self::ReadDir::Regular(tokio::fs::read_dir(self.path).await?),
			AuthKind::Search => Self::ReadDir::Others {
				reader: tokio::fs::read_dir(self.path).await?,
				dir:    Arc::new(self.url.to_owned()),
			},
			AuthKind::Mount | AuthKind::Hub | AuthKind::Scope | AuthKind::Sftp => Err(io::Error::new(
				io::ErrorKind::InvalidInput,
				format!("Not a local URL: {:?}", self.url),
			))?,
		})
	}

	#[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 }

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

	#[inline]

View on GitHub (pinned to 94abcfa92f)

Solutions

  1. Before `read_dir`, check `url.kind()` and route non-Regular/Search kinds to the owning backend.
  2. When creating search URLs over remote/mounted roots, enumerate through that root's engine rather than the local one.
  3. Log the full URL (`{:?}` of `self.url`) at the failure site to identify which kind leaked through.

Example fix

// before
let entries = local.read_dir().await?; // InvalidInput if kind is Mount/Sftp

// after
use yazi_fs::engine::AuthKind;
match local.url().kind() {
    AuthKind::Regular | AuthKind::Search => { let entries = local.read_dir().await?; }
    kind => { /* delegate to the engine registered for `kind` */ }
}
Defensive patterns

Strategy: validation

Validate before calling

use yazi_fs::engine::AuthKind;

match local.url().kind() {
    AuthKind::Regular | AuthKind::Search => { /* safe to read_dir */ }
    kind => { /* delegate listing to the engine owning `kind` */ }
}

Type guard

fn is_listable_locally(kind: AuthKind) -> bool {
    matches!(kind, AuthKind::Regular | AuthKind::Search)
}

Try / catch

match local.read_dir().await {
    Ok(rd) => { /* ... */ }
    Err(e) if e.kind() == io::ErrorKind::InvalidInput => { /* non-local kind leaked in: log url {:?} and re-dispatch */ }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Constructing `Local` from a `Url::Search` whose underlying location is a mount/hub/sftp URL, then calling `read_dir()`; or re-kindling a URL that changed kind between construction and the `read_dir` call.

Common situations: Searching inside an archived or remote directory and then trying to enumerate the results through the local engine; plugins that rewrap search URLs of remote roots into local handles.

Related errors


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