{"record":{"id":"b6d21729f8785b6d","repo":"denoland/deno","slug":"app-name-app-name-resolves-its-runtime-library","errorCode":null,"errorMessage":"app name {app_name:?} resolves its runtime library to {runtime_name}, which is the launcher itself. Choose a different --output name.","messagePattern":"app name (.+?) resolves its runtime library to (.+?), which is the launcher itself\\. Choose a different --output name\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":3106,"sourceCode":"/// `fs::copy` and `fs::rename` both replace silently, so every check has to run\n/// before either does. Returning the paths only on success is what keeps that\n/// ordering intact: a caller cannot reach the paths without having passed the\n/// checks, so a later refactor can't reorder them apart.\nfn resolve_app_dir_targets(\n  app_dir: &Path,\n  app_name: &str,\n  runtime_name: &str,\n  launcher_name: &str,\n  laufey_binary_name: &str,\n) -> Result<AppDirTargets, AnyError> {\n  validate_launcher_name(app_name, \"app name\")?;\n  let dest_dylib = app_dir.join(runtime_name);\n  let launcher_path = app_dir.join(launcher_name);\n  let staged_backend = app_dir.join(laufey_binary_name);\n\n  reject_backend_file_collision(&dest_dylib, app_name, \"runtime library\")?;\n  if dest_dylib == launcher_path {\n    bail!(\n      \"app name {app_name:?} resolves its runtime library to {runtime_name}, \\\n       which is the launcher itself. Choose a different --output name.\",\n    );\n  }\n  // The staged backend is the file we are about to rename *into* the launcher\n  // path, so it colliding with itself is the normal case, not a clash.\n  if launcher_path != staged_backend {\n    reject_backend_file_collision(&launcher_path, app_name, \"launcher\")?;\n  }\n  Ok(AppDirTargets {\n    dest_dylib,\n    launcher_path,\n    staged_backend,\n  })\n}\n\n/// Refuse an app-derived file name that lands on a file the backend shipped.\n///","sourceCodeStart":3088,"sourceCodeEnd":3124,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L3088-L3124","documentation":"When packaging a desktop (laufey) app, `resolve_app_dir_targets` derives both the runtime library name and the launcher name from the app name. On Linux the colocated runtime name is computed by stripping the extension from the app name and appending `.so`; for an app name like `myapp.so` this truncates back to exactly the launcher file name, so the runtime library would overwrite the launcher itself. The check at cli/tools/desktop.rs:3105 detects this identity and bails, telling you to pick a different `--output` name.","triggerScenarios":"Running `deno compile --desktop` (app packaging) with `--output myapp.so` (or any name whose extension-stripped base plus `.so` equals the launcher name) on Linux, so `dest_dylib == launcher_path`.","commonSituations":"Choosing an `.so`-suffixed `--output` name on Linux because the artifact happens to be a shared library; build scripts that append `.so` to the output name; renaming outputs in CI without re-checking desktop packaging constraints.","solutions":["Pick a `--output` name that does not end in `.so` (e.g. `--output myapp` instead of `--output myapp.so`), so the runtime library `myapp.so` differs from the launcher","If the `.so` suffix is added by a build script, remove the suffix before invoking the desktop packager","If the name must keep the suffix, place the output in a different app layout or file the tooling to support alternative runtime-library naming"],"exampleFix":"// before\ndeno compile --desktop --output myapp.so main.ts\n// after\ndeno compile --desktop --output myapp main.ts","handlingStrategy":"validation","validationCode":"fn launcher_and_runtime_collide(app_name: &str) -> bool {\n  let base = match app_name.rfind('.') {\n    Some(dot) if dot > 0 => &app_name[..dot],\n    _ => app_name,\n  };\n  // runtime library is `{base}.so`; the launcher keeps the full app name\n  format!(\"{base}.so\") == app_name\n}\nif launcher_and_runtime_collide(output_name) {\n  eprintln!(\"--output {output_name} collides with its own runtime library; drop the .so suffix\");\n  std::process::exit(2);\n}","typeGuard":"fn safe_desktop_output_name(name: &str) -> bool {\n  !name.ends_with(\".so\") && !name.is_empty()\n}","tryCatchPattern":"match resolve_app_dir_targets(app_dir, app_name, runtime_name, launcher_name, laufey_binary_name) {\n  Ok(targets) => /* proceed with packaging */,\n  Err(e) if e.to_string().contains(\"which is the launcher itself\") => {\n    eprintln!(\"choose an --output name without the .so suffix\");\n    std::process::exit(2);\n  }\n  Err(e) => return Err(e),\n}","preventionTips":["On Linux, never pass an `--output` name ending in `.so` to desktop packaging","Review CI/build scripts that append `.so` or other extensions to output names before packaging","Dry-run the name derivation (strip extension, append `.so`) and compare against the launcher name before packaging","Keep app output names simple (no extension) and let the packager derive the runtime-library name"],"tags":["desktop","packaging","linux","filename-collision","deno-compile"],"backgroundTag":"filename-collision","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-29T08:55:39.519Z","contentChangedAt":"2026-08-29T08:55:39.519Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}