Hmbown/CodeWhale · error
The downloaded app has a different version.
Error message
The downloaded app has a different version.
What it means
After extraction, prepareUpdate() reads CFBundleShortVersionString from the staged app bundle's Info.plist via /usr/libexec/PlistBuddy and requires it to equal update.version. A mismatch (or PlistBuddy failing) means the bundle inside the verified archive does not carry the version the release metadata promised, so the update is aborted and the staging directory removed.
Solutions
- Re-run the update check and retry — if the release was repaired and re-uploaded, the new archive will match its declared version.
- Verify on the release page that the asset's bundle Info.plist CFBundleShortVersionString equals the tag version; if not, the release itself is mis-cut and must be re-published.
- If testing with a locally built zip, rebuild it so the bundle's CFBundleShortVersionString matches the version you pass in the descriptor.
- Confirm the zip contains `<APP_NAME>.app` at its root with a valid Contents/Info.plist.
Defensive patterns
Strategy: validation
Try / catch
try { await prepareUpdate(update); } catch (e) { if (/different version/.test(e.message)) { /* re-check for updates; if it persists, the published release is mis-cut — report it */ } else throw e; } Prevention
- Never hand-repack release zips for production update flows.
- Verify release CI stamps CFBundleShortVersionString with the tag version before publishing.
- Re-run check-for-update after any release repair/re-upload.
When it happens
Trigger: The release zip contains a bundle built from a different commit than the tagged version (mis-cut release); update.version was bumped in metadata without rebuilding the app; PlistBuddy exits non-zero because Info.plist is missing or malformed inside the bundle; the zip's top-level app name differs from APP_NAME so verifyReleaseBundle/pickup picked an unexpected bundle.
Common situations: A release pipeline that re-tagged a version without re-archiving; CI caching a stale .app artifact into the release zip; hand-repacked update zips for testing; a partially uploaded asset from an interrupted release run.
Related errors
- Update the Computer Use app to run the background check.
- Annotated tag did not peel to a commit SHA
- application not found — call list_apps for exact names/pids
- background preview capture failed
- build platform HTTP client
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/8d06b533c19ae78f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/app/updates.mjs:107
for(let redirects=0;redirects<4;redirects++) {
response=await fetch(url,{redirect:"manual",signal:AbortSignal.timeout(60_000)});
if(![301,302,303,307,308].includes(response.status)) break;
const next=new URL(response.headers.get("location"),url);
if(next.protocol!=="https:"||!["github.com","release-assets.githubusercontent.com","objects.githubusercontent.com"].includes(next.hostname)) throw new Error("The update download redirected to an unexpected host.");
url=next.href;
}
if(!response?.ok) throw new Error("The update could not be downloaded. Your current app is unchanged.");
const bytes=await responseBytes(response,update.size);
if(bytes.length!==update.size||crypto.createHash("sha256").update(bytes).digest("hex")!==update.sha256) throw new Error("The update checksum did not match. Your current app is unchanged.");
validateReleaseZip(bytes);
const stage=fs.mkdtempSync(path.join(os.tmpdir(),"codewhale-cu-release-"));
try {
const archive=path.join(stage,"release.zip"); fs.writeFileSync(archive,bytes,{mode:0o600});
const result=spawnSync("ditto",["-x","-k",archive,stage],{encoding:"utf8"});
if(result.status!==0) throw new Error("The update could not be unpacked.");
const bundle=path.join(stage,`${APP_NAME}.app`); verifyReleaseBundle(bundle);
const version=spawnSync("/usr/libexec/PlistBuddy",["-c","Print :CFBundleShortVersionString",path.join(bundle,"Contents","Info.plist")],{encoding:"utf8"});
if(version.status!==0||version.stdout.trim()!==update.version) throw new Error("The downloaded app has a different version.");
return {stage,bundle};
} catch(error) { fs.rmSync(stage,{recursive:true,force:true}); throw error; }
}
export async function restartWithUpdate(prepared,destination) {
const logDir=path.join(os.homedir(),"Library","Logs",APP_NAME); fs.mkdirSync(logDir,{recursive:true});
const log=fs.openSync(path.join(logDir,"update.log"),"a",0o600);
const child=spawn(process.execPath,[fileURLToPath(import.meta.url),"--apply",prepared.bundle,destination,String(process.pid),String(process.ppid)],{detached:true,stdio:["ignore",log,log]});
try { await new Promise((resolve,reject)=>{child.once("spawn",resolve);child.once("error",reject);}); child.unref(); }
finally { fs.closeSync(log); }
}
if(process.argv[1]===fileURLToPath(import.meta.url)&&process.argv[2]==="--apply") {
const [source,destination,owner,launcher]=process.argv.slice(3);
let result;
try {
verifyReleaseBundle(source);
process.kill(Number(owner),"SIGTERM");View on GitHub (pinned to 73e0f67d83)