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

  1. Add the needed directories/patterns to app.security.assetProtocol.scope in tauri.conf.json (e.g. "$HOME/**", "/mnt/**")
  2. Set assetProtocol.enable to true and restart/rebuild the app after config changes
  3. 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

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


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)