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
- Convert the URL to a local path before opening: only call the local engine when `url.as_local()` is `Some(path)`.
- Dispatch by URL kind: route `Sftp`/`Mount`/`Hub`/`Scope` URLs to the engine/backend that owns that kind.
- 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
- Never hand raw URL strings to local-engine APIs; pass them through the URL dispatcher that already switches on URL kind.
- Centralize 'is this URL local?' checks in one helper used by every call site.
- In tests, include a non-local URL case to catch dispatch regressions early.
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
- Not a local URL: {url:?}
- Not a local URL: {:?}
- Cannot write cache stamp: {e}
- Destination already exists
- restore target already exists: {to:?}
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/f1f8e7fe025b9800.
Report an issue: GitHub.