denoland/deno · error

Cannot build a .msi from an empty app directory

Error message

Cannot build a .msi from an empty app directory

What it means

The MSI builder walks the staged app directory and collects every file into install components; an MSI with zero files is meaningless, so the build bails when the walk produced nothing. It means the directory the bundler was pointed at is empty — usually the preceding compile/bundle step failed to place output, or the path is wrong.

Solutions

  1. Verify the app directory actually contains your compiled output (list its files) before the MSI step.
  2. Re-run the preceding build/compile step and confirm it succeeded before packaging.
  3. Check the --output / staging path configuration for typos or wrong job-workspace paths in CI.
Defensive patterns

Strategy: validation

Validate before calling

# Fail the pipeline before the MSI step if the staged app dir is empty
[ -n "$(find "$APP_DIR" -type f -print -quit)" ] || {
  echo "app dir '$APP_DIR' is empty — compile step produced nothing"; exit 1;
}

Prevention

When it happens

Trigger: `deno desktop` packaging a .msi where the app directory contains no files: an earlier build stage failed silently, the output/staging path is misconfigured, or a cleanup step emptied it between stages.

Common situations: CI pipelines where the compile step and packaging step are separate jobs and artifacts didn't transfer; wrong --output path pointing at a freshly-created empty dir.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20). Data as JSON: /api/errors/513c518ce3bb07f6. Report an issue: GitHub.

Appendix: source

Thrown at cli/tools/desktop.rs:4746

    let long = rel
      .file_name()
      .map(|s| s.to_string_lossy().into_owned())
      .unwrap_or_else(|| key.clone());
    short_counter += 1;
    files.push(MsiFile {
      key,
      component,
      file_name: format!(
        "{}|{}",
        msi_short_name(short_counter, &long, false),
        long
      ),
      size: *size,
      abs_path: app_dir.join(rel),
    });
  }
  if files.is_empty() {
    bail!("Cannot build a .msi from an empty app directory");
  }

  // Locate the laufey launcher in the install root so we can author a Start Menu
  // shortcut to it (otherwise the installed app is not discoverable — there is no
  // icon anywhere, only files under Program Files). The launcher is the backend
  // binary renamed to `<app>.exe`; it auto-loads the co-located `<app>.dll`
  // runtime, so the shortcut targets it directly with no arguments.
  let launcher_exe = format!("{app_name}.exe").to_ascii_lowercase();
  let shortcut_target = rel_files
    .iter()
    .zip(files.iter())
    .find(|((rel, _), _)| {
      rel.parent() == Some(Path::new(""))
        && rel
          .file_name()
          .and_then(|n| n.to_str())
          .is_some_and(|n| n.to_ascii_lowercase() == launcher_exe)
    })

View on GitHub (pinned to f7822238ca)