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

  1. Ensure the file was entered through the plugin's mount/entry API so the URL is a Mount/Hub/Scope variant
  2. Verify the custom filesystem is actually mounted before resolving files into it
  3. Check code that constructs URLs manually and use the VFS factory/registry instead of raw Url construction
  4. 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

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


AI-assisted analysis of sxyazi/yazi@5f901b886b (2026-09-02). Data as JSON: /api/errors/0dbbb27f359308bc. Report an issue: GitHub.