sxyazi/yazi · error · io::Error
empty file stem
Error message
empty file stem
What it means
`_unique_file` generates a non-colliding filename (e.g. `name (1).ext`) for copy/paste or download dedup, and it requires the URL's file stem to be non-empty. If `url.stem()` yields None — a path ending in a separator, a dot-only tail, or otherwise empty stem — the function aborts with InvalidInput instead of inventing a name.
Source
Thrown at yazi-vfs/src/fns.rs:28
Ok(_) => true,
Err(e) => e.kind() != io::ErrorKind::NotFound,
}
}
pub async fn unique_file(u: UrlBuf, is_dir: bool) -> io::Result<UrlBuf> {
let result =
if is_dir { engine::create_dir(&u).await } else { engine::create_new(&u).await.map(|_| ()) };
match result {
Ok(()) => Ok(u),
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => _unique_file(u, is_dir).await,
Err(e) => Err(e),
}
}
async fn _unique_file(mut url: UrlBuf, is_dir: bool) -> io::Result<UrlBuf> {
let Some(stem) = url.stem().owned() else {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "empty file stem"));
};
let dot_ext = match url.ext() {
Some(e) => {
let mut s = StrandBuf::with_capacity(url.loc().kind(), e.len() + 1);
s.push_str(".");
s.try_push(e)?;
s
}
None => StrandBuf::default(),
};
let mut name = StrandBuf::with_capacity(url.loc().kind(), stem.len() + dot_ext.len() + 5);
for i in 1u64.. {
name.clear();
name.try_push(&stem)?;
if is_dir {View on GitHub (pinned to 8ebf930f17)
Solutions
- Strip trailing separators and ensure the URL has a real file name before calling unique_file
- If the source has no name, synthesize one (e.g. 'download', 'folder') before invoking the API
- Validate with `url.file_name()`/stem on the caller side and return a clear user-facing error otherwise
- Check upstream producers (clipboard, download job) for why the name is empty
Example fix
-- before
local unique = vfs.unique_file(url) -- url ends with '/'
-- after
if not url:stem() then url = url:join('unnamed') end
local unique = vfs.unique_file(url) Defensive patterns
Strategy: validation
Validate before calling
local tail = url:name() or url:urn() assert(tail ~= '' and tail ~= '/' and url:stem(), 'unique_file needs a non-empty file stem, got: '..tostring(tail))
Try / catch
local ok, unique = pcall(function() return vfs.unique_file(url, is_dir) end)
if not ok then
-- 'empty file stem' (InvalidInput): synthesize a name and retry
url = url:join('unnamed')
unique = vfs.unique_file(url, is_dir)
end Prevention
- Strip trailing separators from URLs before dedup/name-generation calls
- Never pass empty-tailed or programmatically joined-empty URLs to unique_file
- Synthesize fallback names ('download', 'unnamed') when the source provides none
- Audit producers (clipboard paste, download jobs) to guarantee they always yield a named entry
When it happens
Trigger: Calling `unique_file(url, is_dir)` with a URL whose tail has no stem: a trailing slash (directory tail without a name), a name consisting only of an extension separator, or an empty last component produced by joining paths programmatically.
Common situations: Pasting/downloads where the remote name was empty or slash-terminated, drag-drop payloads with bare directory URLs, or code constructing `dir.join("")` and passing the result to unique_file.
Related errors
- Not a custom VFS URL: {url:?}
- invalid trash item path
- Invalid new or old file name
- Not a SFTP URL: {url:?}
- Cannot construct system event
AI-assisted analysis of sxyazi/yazi@8ebf930f17 (2026-09-02).
Data as JSON: /api/errors/f6a63e7eb66b821e.
Report an issue: GitHub.