Hmbown/CodeWhale · error · Error
The update checksum did not match. Your current app is…
Error message
The update checksum did not match. Your current app is unchanged.
What it means
prepareUpdate() verifies both the downloaded byte length and the SHA-256 digest of the archive against the values from the update descriptor (update.size and update.sha256). A mismatch means the downloaded bytes differ from what the release metadata promised, so the archive is discarded and the installed app is left unchanged.
Solutions
- Retry the update — a fresh download usually fixes transient truncation or corruption.
- Re-run the update check to refresh update.size/sha256 in case the release asset was re-published with different bytes.
- Bypass any content-modifying proxy or TLS interceptor and download over a direct connection.
- Compare the asset's published SHA-256 on the GitHub release page against update.sha256 to confirm which side is stale.
Defensive patterns
Strategy: retry
Try / catch
try { await prepareUpdate(update); } catch (e) { if (/checksum did not match/.test(e.message)) { /* refresh descriptor via check-for-update and retry once on a stable connection */ } else throw e; } Prevention
- Update over stable networks; avoid captive portals and content-modifying proxies.
- Re-run check-for-update if a release asset was re-published.
- Never bypass the checksum check; treat mismatches as transient or as a compromised release.
When it happens
Trigger: A truncated or interrupted download (responseBytes returned fewer bytes than update.size); a proxy or middlebox modifying the payload; downloading a re-uploaded asset whose content changed after the descriptor's sha256 was computed; bit-level corruption in transit.
Common situations: Flaky Wi-Fi or mobile connections dropping bytes; corporate TLS proxies that re-compress or inspect content; a release asset replaced in place without updating the published checksum; disk-full conditions during buffering.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Checksum mismatch for
- Checksum manifest is missing
- Fleet artifact checksum does not match the recorded receipt
- imported content digest mismatch after copy; aborted
- Invalid checksum manifest line
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/789131b4b5d07c1f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/app/updates.mjs:98
if(position!==end) throw new Error("Invalid update archive length.");
return count;
}
export async function prepareUpdate(update) {
if(!update?.available) throw new Error("Check for an available update first.");
if(!newerVersion(update.version,APP_VERSION)||update.url!==`${repository}/releases/download/v${update.version}/Codewhale-Computer-Use-${update.version}-macos-universal.zip`||!Number.isSafeInteger(update.size)||update.size<=0||update.size>limit) throw new Error("The update identity is invalid.");
// Only GitHub's fixed release URL and its asset CDN can serve the bytes.
let url=update.url, response;
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(); }View on GitHub (pinned to 73e0f67d83)