{"record":{"id":"878f817eb1452eab","repo":"tauri-apps/tauri","slug":"missing-os-permission-to-access-path-path-e","errorCode":null,"errorMessage":"Missing OS permission to access path \"{path}\": {e}","messagePattern":"Missing OS permission to access path \"(.+?)\": (.+?)","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"crates/tauri/src/protocol/asset.rs","lineNumber":66,"sourceCode":"    return resp.status(403).body(Vec::new().into()).map_err(Into::into);\n  }\n\n  // Separate block for easier error handling\n  let mut file = match File::open(path.clone()) {\n    Ok(file) => file,\n    Err(e) => {\n      #[cfg(target_os = \"android\")]\n      {\n        if path.starts_with(\"/storage/emulated/0/Android/data/\") {\n          log::error!(\"Failed to open Android external storage file '{path}': {e}. This may be due to missing storage permissions.\");\n        }\n      }\n      return if e.kind() == std::io::ErrorKind::NotFound {\n        log::error!(\"File does not exist at path: {path}\");\n        return resp.status(404).body(Vec::new().into()).map_err(Into::into);\n      } else if e.kind() == std::io::ErrorKind::PermissionDenied {\n        log::error!(\"Missing OS permission to access path \\\"{path}\\\": {e}\");\n        return resp.status(403).body(Vec::new().into()).map_err(Into::into);\n      } else {\n        Err(e.into())\n      };\n    }\n  };\n\n  let len = file.metadata()?.len();\n  let (mime_type, read_bytes) = {\n    // get file mime type\n    let nbytes = len.min(8192);\n    let mut magic_buf = Vec::with_capacity(nbytes as usize);\n    (&mut file).take(nbytes).read_to_end(&mut magic_buf)?;\n    file.rewind()?;\n    (\n      MimeType::parse(&magic_buf, &path),\n      // return the `magic_bytes` if we read the whole file\n      // to avoid reading it again later if this is not a range request\n      if len < 8192 { Some(magic_buf) } else { None },","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/tauri-apps/tauri/blob/52e4b6e71d8632a7e648f866c442e287ecddee34/crates/tauri/src/protocol/asset.rs#L48-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (Android)\n// fetch(convertFileSrc('/storage/emulated/0/Android/data/.../clip.mp4'))\n// -> 403 PermissionDenied + 'missing storage permissions' log\n\n// after\n// 1. AndroidManifest.xml: <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n// 2. request at runtime, or copy into app data first:\n//   adb shell run-as <pkg> cp /storage/emulated/0/... files/clip.mp4","handlingStrategy":"fallback","validationCode":"import { stat } from '@tauri-apps/plugin-fs';\n// preflight readability; PermissionDenied surfaces here with a clearer context\ntry { await stat(filePath); } catch (e) { console.warn('file not readable by app:', e); }","typeGuard":null,"tryCatchPattern":"const res = await fetch(convertFileSrc(filePath));\nif (res.status === 403) {\n  // OS denies the read: fall back to a Rust command that copies the file into $APPDATA via a picker\n  await invoke('copy_into_app_data', { path: filePath });\n}","preventionTips":["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`"],"tags":["asset-protocol","permissions","http-403","android","macos-sandbox","filesystem"],"backgroundTag":"os-permission-denied","analyzedSha":"52e4b6e71d8632a7e648f866c442e287ecddee34","analyzedAt":"2026-08-20T13:59:20.734Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}