tauri-apps/tauri · warning
asset protocol not configured to allow the path
Error message
asset protocol not configured to allow the path: {path} What it means
The asset protocol enforces the configured asset scope: only paths matching the app's assetProtocol scope (allowlist) in tauri.conf.json are served. When the requested path is a valid path but not allowed by the scope, the handler logs this error and returns 403.
Solutions
- Add the needed directories/patterns to app.security.assetProtocol.scope in tauri.conf.json (e.g. "$HOME/**", "/mnt/**")
- Set assetProtocol.enable to true and restart/rebuild the app after config changes
- Have the app copy or stream files outside the scope via a custom Tauri command instead of the asset protocol
Example fix
// before (tauri.conf.json)
"assetProtocol": { "enable": true, "scope": [] }
// after
"assetProtocol": { "enable": true, "scope": ["$HOME/**", "$DESKTOP/**"] } Defensive patterns
Strategy: validation
Validate before calling
// tauri.conf.json check before shipping
"app": { "security": { "assetProtocol": { "enable": true, "scope": ["$HOME/**", "$DESKTOP/**"] } } } Type guard
function isWithinScope(path: string, scopePrefixes: string[]): boolean {
return scopePrefixes.some(pfx => path.startsWith(pfx.replace('$HOME', homeDir)));
} Try / catch
const resp = await fetch(assetUrl);
if (resp.status === 403) {
// likely scope denial; fall back to a custom command that reads the file
const data = await invoke('read_external_file', { path });
} Prevention
- Keep the assetProtocol scope in sync with every directory your app reads
- Test file-dialog-picked paths against the scope during development
- Prefer copying files into the app data dir instead of serving them from arbitrary locations
When it happens
Trigger: A webview request to asset:// for a filesystem path that is not covered by app.security.assetProtocol.scope (with the builtin asset protocol enabled), evaluated in get_response via scope.is_allowed().
Common situations: Developer enables the asset protocol but forgets to add the directory to scope; user picks a file outside the configured scope glob via a file dialog; scope patterns too narrow ($HOME/** missing, wrong glob syntax).
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- asset protocol path " " is not valid
- cannot use both `resources` and `resources_map`
- Language not found. It must be one of
- Missing OS permission to access path
- The `frontendDist` configuration is set to
AI-assisted analysis of tauri-apps/tauri@460ec35447 (2026-09-18).
Data as JSON: /api/errors/d69bc7af3e3765a8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tauri/src/protocol/asset.rs:48
request: Request<Vec<u8>>,
scope: &scope::fs::Scope,
window_origin: &str,
) -> Result<Response<Cow<'static, [u8]>>, Box<dyn std::error::Error>> {
// skip leading `/`
let path = percent_encoding::percent_decode(&request.uri().path().as_bytes()[1..])
.decode_utf8_lossy()
.to_string();
let mut resp = Response::builder().header("Access-Control-Allow-Origin", window_origin);
if let Err(e) = SafePathBuf::new(path.clone().into()) {
log::error!("asset protocol path \"{path}\" is not valid: {e}");
return resp.status(403).body(Vec::new().into()).map_err(Into::into);
}
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 {View on GitHub (pinned to 460ec35447)