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

  1. Verify `src_dir` exists before bundling: run `std::fs::metadata(src_dir)` yourself and report a clear error
  2. Check the configured bundle output path in tauri.conf.json / CLI flags points at the produced artifact
  3. Fix filesystem permissions on the path and its parents (read + execute bits) for the user running the bundler
  4. 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

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


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.app

View on GitHub (pinned to 460ec35447)