tauri-apps/tauri · error
Missing OS permission to access path "{path}": {e}
Error message
Missing OS permission to access path "{path}": {e} What it means
Runtime 403 from the asset protocol handler. File::open failed with std::io::ErrorKind::PermissionDenied, i.e. the operating system (not the Tauri scope) refused the read. The surrounding code even has an Android-specific hint for paths under /storage/emulated/0/Android/data/ pointing at missing storage permissions.
Source
Thrown at crates/tauri/src/protocol/asset.rs:66
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),
// return the `magic_bytes` if we read the whole file
// to avoid reading it again later if this is not a range request
if len < 8192 { Some(magic_buf) } else { None },View on GitHub (pinned to 52e4b6e71d)
Solutions
- Android: declare the needed storage permissions in AndroidManifest.xml and request them at runtime, or copy the file into app-accessible storage ($APPDATA) via a share intents / document picker
- macOS: add the appropriate entitlement (user-selected read-only, or disable the sandbox) in the bundle settings / Xcode capabilities
- Linux/other: fix ownership or mode of the file (chmod/chown) so the app's user can read it
Example fix
// before (Android)
// fetch(convertFileSrc('/storage/emulated/0/Android/data/.../clip.mp4'))
// -> 403 PermissionDenied + 'missing storage permissions' log
// after
// 1. AndroidManifest.xml: <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
// 2. request at runtime, or copy into app data first:
// adb shell run-as <pkg> cp /storage/emulated/0/... files/clip.mp4 Defensive patterns
Strategy: fallback
Validate before calling
import { stat } from '@tauri-apps/plugin-fs';
// preflight readability; PermissionDenied surfaces here with a clearer context
try { await stat(filePath); } catch (e) { console.warn('file not readable by app:', e); } Try / catch
const res = await fetch(convertFileSrc(filePath));
if (res.status === 403) {
// OS denies the read: fall back to a Rust command that copies the file into $APPDATA via a picker
await invoke('copy_into_app_data', { path: filePath });
} Prevention
- Keep assets inside app-accessible dirs ($APPDATA/$RESOURCE) instead of OS-protected locations
- On Android use the document picker / share intent flow rather than raw /storage paths
- On macOS decide sandbox entitlements up front and test reads from a signed build, not just `tauri dev`
When it happens
Trigger: Android scoped storage: reading /storage/emulated/0/... without READ/MANAGE_EXTERNAL_STORAGE; macOS App Sandbox without a read entitlement or user-selected file access; files owned by another user or with 0600 perms on Linux; running the app in a hardened sandbox.
Common situations: Android 11+ scoped storage restrictions on shared storage; macOS app sandbox enabled in Xcode without com.apple.security.files.user-selected.read-only; files created by a root/sudo process then read by the app; enterprise locked-down machines.
Related errors
- asset protocol path "{path}" is not valid: {e}
- asset protocol not configured to allow the path: {path}
- File does not exist at path: {path}
- unable to create autogenerated commands dir
- failed to create plugin documentation directory
AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20).
Data as JSON: /api/errors/878f817eb1452eab.
Report an issue: GitHub.