tauri-apps/tauri · error
File does not exist at path: {path}
Error message
File does not exist at path: {path} What it means
Runtime 404 from the asset protocol handler. The path was valid and allowed by the asset scope, but File::open failed with std::io::ErrorKind::NotFound, so Tauri logs this message and returns an empty 404 body.
Source
Thrown at crates/tauri/src/protocol/asset.rs:63
if !scope.is_allowed(&path) {
log::error!("asset protocol not configured to allow the path: {path}");
return resp.status(403).body(Vec::new().into()).map_err(Into::into);
}
// Separate block for easier error handling
let mut file = match File::open(path.clone()) {
Ok(file) => file,
Err(e) => {
#[cfg(target_os = "android")]
{
if path.starts_with("/storage/emulated/0/Android/data/") {
log::error!("Failed to open Android external storage file '{path}': {e}. This may be due to missing storage permissions.");
}
}
return if e.kind() == std::io::ErrorKind::NotFound {
log::error!("File does not exist at path: {path}");
return resp.status(404).body(Vec::new().into()).map_err(Into::into);
} else if e.kind() == std::io::ErrorKind::PermissionDenied {
log::error!("Missing OS permission to access path \"{path}\": {e}");
return resp.status(403).body(Vec::new().into()).map_err(Into::into);
} else {
Err(e.into())
};
}
};
let len = file.metadata()?.len();
let (mime_type, read_bytes) = {
// get file mime type
let nbytes = len.min(8192);
let mut magic_buf = Vec::with_capacity(nbytes as usize);
(&mut file).take(nbytes).read_to_end(&mut magic_buf)?;
file.rewind()?;
(
MimeType::parse(&magic_buf, &path),View on GitHub (pinned to 52e4b6e71d)
Solutions
- Check existence before fetching using the fs plugin: `import { exists } from '@tauri-apps/plugin-fs'; if (await exists(path)) ...`
- Verify the exact filename (case, extension) matches what is on disk
- Regenerate or re-download the expected file if the producer step was skipped or failed
Example fix
// before
const src = convertFileSrc(filePath);
const blob = await (await fetch(src)).blob(); // 404, empty body
// after
import { exists } from '@tauri-apps/plugin-fs';
if (!(await exists(filePath))) throw new Error(`missing asset: ${filePath}`);
const res = await fetch(convertFileSrc(filePath));
if (!res.ok) throw new Error(`asset protocol ${res.status}`); Defensive patterns
Strategy: validation
Validate before calling
import { exists } from '@tauri-apps/plugin-fs';
async function assetOrPlaceholder(path, placeholder) {
return (await exists(path)) ? convertFileSrc(path) : placeholder;
} Try / catch
const res = await fetch(convertFileSrc(filePath));
if (res.status === 404) return fallbackAvatar; // graceful degradation
if (!res.ok) throw new Error(`asset protocol error ${res.status}`); Prevention
- Check existence with the fs plugin before building asset URLs
- Treat asset URLs as ephemeral: re-derive them from a current file listing instead of caching
- Watch filename casing when developing on macOS but shipping to Linux/Android
When it happens
Trigger: Fetching convertFileSrc(path) for a file that has been deleted, not yet written, or spelled differently than on disk (wrong case on case-sensitive filesystems, wrong extension, trailing spaces).
Common situations: Referencing build artifacts or downloads that are produced asynchronously and not ready yet; moving/renaming files after the URL was built; developing case-insensitively on macOS and deploying to a case-sensitive Linux/Android filesystem; caching stale asset URLs.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- asset protocol path "{path}" is not valid: {e}
- Missing OS permission to access path "{path}": {e}
- No file found in {} matching {}
- source does not exist
- asset protocol not configured to allow the path: {path}
AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20).
Data as JSON: /api/errors/76d8f0bc38f2df4a.
Report an issue: GitHub.