sxyazi/yazi · error · io::Error
Not a custom VFS URL: {url:?}
Error message
Not a custom VFS URL: {url:?} What it means
This error comes from the Lua VFS engine's mount constructor: it pattern-matches the incoming URL against the custom-VFS variants (Mount, Hub, Scope) and rejects anything else with InvalidInput. It means the URL was handed to the Lua VFS engine but does not actually belong to a Lua-backed custom filesystem. The developer likely registered or routed the URL incorrectly.
Source
Thrown at yazi-vfs/src/engine/lua/lua.rs:112
async fn hard_link<P>(&self, to: P) -> io::Result<()>
where
P: DynPath,
{
let from = self.url.to_owned();
let to = to.dyn_path().to_owned();
Ok(self.call(ProvideJob::HardLink { from, to }).await?.ok()?)
}
async fn metadata(&self) -> io::Result<Cha> {
let url = self.url.to_owned();
Ok(self.call(ProvideJob::Metadata { url }).await?.0?)
}
async fn new<'b>(url: Url<'b>) -> io::Result<Self::Me<'b>> {
let (Url::Mount { auth, .. } | Url::Hub { auth, .. } | Url::Scope { auth, .. }) = url else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Not a custom VFS URL: {url:?}"),
));
};
let service = Vfs::service::<&ServiceLua>(auth)?;
Ok(Self::Me { url, service })
}
async fn read_dir(self) -> io::Result<Self::ReadDir> {
let url = self.url.to_owned();
let entries: Vec<DirEntry> = self.call(ProvideJob::ReadDir { url }).await?.0?;
Ok(ReadDir { entries: entries.into_iter() })
}
async fn read_link(&self) -> io::Result<PathBufDyn> {
let url = self.url.to_owned();View on GitHub (pinned to 5f901b886b)
Solutions
- Ensure the file was entered through the plugin's mount/entry API so the URL is a Mount/Hub/Scope variant
- Verify the custom filesystem is actually mounted before resolving files into it
- Check code that constructs URLs manually and use the VFS factory/registry instead of raw Url construction
- Inspect the URL kind (log `url:kind()`) before passing it to the Lua VFS engine
Example fix
// before
let file = vfs::File::new(Url::from regular path)?;
// after
let url = my_plugin.mounted_url(path)?; // returns Url::Mount { .. }
let file = vfs::File::new(url)?; Defensive patterns
Strategy: type-guard
Validate before calling
local kind = url:kind() assert(kind == 'mount' or kind == 'hub' or kind == 'scope', 'URL is not a custom VFS URL: '..tostring(kind))
Type guard
fn is_custom_vfs_url(url: &Url) -> bool {
matches!(url, Url::Mount { .. } | Url::Hub { .. } | Url::Scope { .. })
} Try / catch
match LuaVfs::new(url).await {
Ok(f) => f,
Err(e) if e.kind() == io::ErrorKind::InvalidInput && e.to_string().contains("Not a custom VFS URL") => {
// route to the correct engine instead
return generic_engine::open(url).await;
}
Err(e) => return Err(e),
} Prevention
- Only construct file handles through the VFS dispatcher so URL kind selects the engine
- Ensure custom filesystems are mounted before resolving their paths into file handles
- Log url kind at debug boundaries when plumbing URLs between plugins and the core
- After upgrades, audit manual Url constructions against the current variant set
When it happens
Trigger: Calling `Vfs::new(url)` / engine entry points for the Lua backend with a plain local, Sftp, Archive, Search, or regular-file URL instead of a Url::Mount/Hub/Scope carrying custom-VFS auth.
Common situations: A plugin forgetting to wrap paths in the proper custom VFS URL, a previewer/fetcher resolving to an unmounted custom-fs location, or a regression in URL construction after a Yazi version that changed URL variants.
Related errors
- Not a SFTP URL: {url:?}
- empty file stem
- InvalidInput
- either name or interactive must be specified in TabRenameFor
- not a URL
AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02).
Data as JSON: /api/errors/0dbbb27f359308bc.
Report an issue: GitHub.