sxyazi/yazi · error · io::Error
Not a local URL: {url:?}
Error message
Not a local URL: {url:?} What it means
`Local::new(url)` (yazi-fs/src/engine/local/local.rs:91) constructs a local-filesystem handle only from `Url::Regular` and `Url::Search` variants. Any other variant (`Mount`, `Hub`, `Scope`, `Sftp`) is rejected with `ErrorKind::InvalidInput` and the message `Not a local URL: {url:?}`. The constructor is the single gatekeeper that keeps non-local URLs out of the local engine.
Source
Thrown at yazi-fs/src/engine/local/local.rs:91
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>> {
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),
))?,
})
}View on GitHub (pinned to 94abcfa92f)
Solutions
- Match on the URL before constructing: handle `Url::Regular(loc) | Url::Search { loc, .. }` with the local engine and send other kinds to their own backend.
- Check `url.kind()` against `AuthKind::Regular | AuthKind::Search` as a pre-condition.
- Fix the upstream URL construction so only local paths reach this code.
Example fix
// before
let local = Local::new(url).await?; // Err for Url::Sftp
// after
match url {
Url::Regular(loc) | Url::Search { loc, .. } => { let local = Local::new(url).await?; /* ... */ }
_ => { /* use the engine for Mount/Hub/Scope/Sftp */ }
} Defensive patterns
Strategy: type-guard
Validate before calling
use yazi_fs::engine::AuthKind;
if !matches!(url.kind(), AuthKind::Regular | AuthKind::Search) {
// do not construct Local from this URL
} Type guard
fn accepts_local(url: &Url) -> bool {
matches!(url, Url::Regular(_) | Url::Search { .. })
} Try / catch
match Local::new(url).await {
Ok(me) => { /* ... */ }
Err(e) if e.kind() == io::ErrorKind::InvalidInput => { /* re-dispatch by url.kind() */ }
Err(e) => return Err(e),
} Prevention
- Construct backend handles only through the engine registry that selects on URL kind.
- Keep a single match over the Url enum as the construction gate; do not duplicate it per call site.
- Treat 'Not a local URL' errors as programming bugs (wrong dispatch), not runtime noise.
When it happens
Trigger: Calling `Local::new(url)` (or `read_dir`/metadata helpers that build a `Local` first) with a `Url` parsed from `sftp://…`, `mount://…`, a hub/scope URL, or a test URL like `test-hub://a1/@root/a`.
Common situations: A plugin or test iterates URLs of mixed kinds and feeds each to the local engine without filtering; a config or script embeds a remote URL string that parses into a non-`Regular` variant.
Related errors
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/6b2da7e3536b50b8.
Report an issue: GitHub.