Hmbown/CodeWhale · error · Error
The installation destination contains a different…
Error message
The installation destination contains a different application.
What it means
replaceMacBundle refuses to overwrite an existing destination bundle unless it looks like this app's bundle (it must contain Contents/Resources/plugin/app/daemon.mjs). This guards against clobbering an unrelated .app the user placed at the same path, which would destroy another application's data.
Solutions
- Remove or move the unrelated item at destination, or point destination at a free path
- Verify the destination is the correct install location and contains the expected daemon.mjs marker if it should be this app
- If it is a stale install of this app, reinstall cleanly from scratch rather than replacing
- Confirm bundle layout matches the current app structure (Contents/Resources/plugin/app/daemon.mjs)
Example fix
// before code.replaceMacBundle(stagedBundle, "/Applications/SomeOther.app"); // after code.replaceMacBundle(stagedBundle, "/Applications/Codewhale Computer Use.app");
Defensive patterns
Strategy: validation
Validate before calling
const marker = path.join(destination, "Contents", "Resources", "plugin", "app", "daemon.mjs");
if (fs.existsSync(destination) && !fs.existsSync(marker)) {
throw new Error("destination holds a different app");
} Try / catch
try {
replaceMacBundle(src, dest);
} catch (e) {
if (e.message.includes("different application")) {
promptUser(`Move the existing app at ${dest} before installing.`);
} else throw e;
} Prevention
- Use a dedicated, app-specific install path to avoid collisions
- Check destination contents before every update
- Never point the installer at generic paths without a fixed app name
- Detect foreign bundles early and surface a user-facing move/rename prompt
When it happens
Trigger: Calling replaceMacBundle(source, destination) where destination exists but lacks the daemon.mjs marker file — i.e. a different .app bundle, a plain directory, or a bundle from a differently-structured app sits at the install path.
Common situations: Install destination changed to a path that already hosts another app (e.g. a name collision under /Applications); an old install layout predating the plugin/app directory structure; a partially deleted bundle missing its resources.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- The installation destination must not be a symlink.
- application not found — call list_apps for exact names/pids
- background preview capture failed
- bundle already installed; use
- bundle exceeds the file limit
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/85bccb767f8f155f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/app/install-macos.mjs:10
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";
import { spawnSync } from "node:child_process";
/** Publish a verified bundle in one rename, retaining the previous install. */
export function replaceMacBundle(source, destination, { prepare = () => {}, verify = verifySignature } = {}) {
const parent = path.dirname(destination);
fs.mkdirSync(parent, { recursive: true });
if (fs.existsSync(destination) && !fs.existsSync(path.join(destination, "Contents", "Resources", "plugin", "app", "daemon.mjs"))) throw new Error("The installation destination contains a different application.");
if (fs.existsSync(destination) && fs.lstatSync(destination).isSymbolicLink()) throw new Error("The installation destination must not be a symlink.");
const staging = fs.mkdtempSync(path.join(parent, ".codewhale-cu-update-"));
const next = path.join(staging, path.basename(destination));
let backup = null;
try {
fs.cpSync(source, next, { recursive: true });
prepare(next);
verify(next);
if (fs.existsSync(destination)) {
const backups = path.join(parent, ".codewhale-cu-backups");
fs.mkdirSync(backups, { recursive: true, mode: 0o700 });
backup = path.join(backups, `${Date.now()}-${crypto.randomUUID()}.app`);
fs.renameSync(destination, backup);
}
try { fs.renameSync(next, destination); }
catch (error) { if (backup) fs.renameSync(backup, destination); throw error; }
return { backup };
} finally { fs.rmSync(staging, { recursive: true, force: true }); }View on GitHub (pinned to 73e0f67d83)