tauri-apps/tauri · error
Can't read source directory
Error message
Can't read source directory
What it means
Panic from `.expect("Can't read source directory")` on `fs::metadata(src_dir)` in the Linux `create_tar_from_src`. `fs::metadata` returns `Err` (and the expect panics) when the source path does not exist, permission is denied on a parent directory, or the path is invalid. The bundler needs the file type to decide between single-file and directory tar packaging, so it treats a failed stat as fatal.
Solutions
- Verify `src_dir` exists before bundling: run `std::fs::metadata(src_dir)` yourself and report a clear error
- Check the configured bundle output path in tauri.conf.json / CLI flags points at the produced artifact
- Fix filesystem permissions on the path and its parents (read + execute bits) for the user running the bundler
- If the path is produced by a previous build step, ensure that step succeeded and completed before invoking the bundler
Example fix
// before
create_tar("target/release/bundle/app", dest)?; // panics if missing
// after
let src = Path::new("target/release/bundle/app");
let meta = std::fs::metadata(src)
.map_err(|e| anyhow!("updater source missing: {e}"))?;
create_tar(src, dest)?; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_readable_dir(p: &std::path::Path) -> Result<(), String> {
match std::fs::metadata(p) {
Ok(m) if m.is_dir() => Ok(()),
Ok(_) => Err(format!("not a directory: {}", p.display())),
Err(e) => Err(format!("cannot stat {}: {}", p.display(), e)),
}
}
// call before create_tar on Linux Type guard
fn is_readable_dir(p: &std::path::Path) -> bool {
std::fs::metadata(p).map(|m| m.is_dir()).unwrap_or(false)
} Try / catch
match std::fs::metadata(src_dir) {
Ok(meta) if meta.is_dir() => create_tar(src_dir, dest)?,
Ok(_) => return Err(anyhow!("updater source is not a directory")),
Err(e) => return Err(anyhow!("updater source missing/unreadable: {e}")),
} Prevention
- Check the source exists with fs::metadata before bundling
- Ensure previous build steps completed and produced the artifact
- Fix permissions (r+x on directories) for the user running the bundler
- Verify bundle output paths in tauri.conf.json match where artifacts are actually written
When it happens
Trigger: Calling `create_tar`/`create_tar_from_src` on Linux when `src_dir` does not exist on disk, is unreadable due to filesystem permissions, is a broken symlink resolved before stat, or the process lacks search permission on a path component.
Common situations: The updater artifact was deleted or moved between build steps, a CI cache miss left the bundle directory absent, AppImage/deb output path misconfigured in tauri.conf.json, or running the bundler in a container with restricted volume permissions.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- Can't extract file name from path
- failed to canonicalize global API script path
- failed to extract external binary filename
- failed to get exe directory
- failed to read kotlin file as string
AI-assisted analysis of tauri-apps/tauri@460ec35447 (2026-09-18).
Data as JSON: /api/errors/cd80c6f832e85c40.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tauri-bundler/src/bundle/updater_bundle.rs:259
Ok(dest_path.to_owned())
}
#[cfg(target_os = "macos")]
fn create_tar_from_src<P: AsRef<Path>, W: Write>(src_dir: P, dest_file: W) -> crate::Result<W> {
let src_dir = src_dir.as_ref();
let mut builder = tar::Builder::new(dest_file);
builder.follow_symlinks(false);
builder.append_dir_all(src_dir.file_name().expect("Path has no file_name"), src_dir)?;
builder.into_inner().map_err(Into::into)
}
#[cfg(target_os = "linux")]
fn create_tar_from_src<P: AsRef<Path>, W: Write>(src_dir: P, dest_file: W) -> crate::Result<W> {
let src_dir = src_dir.as_ref();
let mut tar_builder = tar::Builder::new(dest_file);
// validate source type
let file_type = fs::metadata(src_dir).expect("Can't read source directory");
// if it's a file don't need to walkdir
if file_type.is_file() {
let mut src_file = fs::File::open(src_dir)?;
let file_name = src_dir
.file_name()
.expect("Can't extract file name from path");
tar_builder.append_file(file_name, &mut src_file)?;
} else {
for entry in walkdir::WalkDir::new(src_dir) {
let entry = entry?;
let src_path = entry.path();
if src_path == src_dir {
continue;
}
// We add the .parent() because example if we send a path
// /dev/src-tauri/target/debug/bundle/osx/app.appView on GitHub (pinned to 460ec35447)