Hmbown/CodeWhale · critical · Error

The app signature did not verify

Error message

The app signature did not verify: ${result.stderr?.trim() ?? "codesign unavailable"}

What it means

verifySignature runs `codesign --verify --deep --strict` on the staged bundle and throws when the codesign exit status is non-zero. This is the first line of defense ensuring the update binary is signed and untampered; the thrown message includes codesign's stderr for diagnosis.

Solutions

  1. Re-download the release from the official source and re-verify (rules out corruption/tampering)
  2. Sign the bundle with a valid Developer ID before distribution: codesign --deep --force --sign 'Developer ID Application: ...'
  3. Inspect the stderr included in the message (e.g. 'code object is not signed at all', 'expired') and address the specific codesign failure
  4. Ensure macOS command line tools are installed so /usr/bin/codesign exists and works

Example fix

// before (unsigned dev build goes straight to verify)
code.verifyReleaseBundle(staged);
// after
code.spawnSync("codesign", ["--deep", "--force", "--sign", process.env.DEV_ID, staged]);
code.verifyReleaseBundle(staged);
Defensive patterns

Strategy: try-catch

Validate before calling

const probe = spawnSync("codesign", ["--verify", "--deep", "--strict", bundle], { encoding: "utf8" });
if (probe.status !== 0) console.error("signature check will fail:", probe.stderr);

Try / catch

try {
  verifySignature(bundle);
} catch (e) {
  if (e.message.startsWith("The app signature did not verify")) {
    console.error("Rejecting bundle:", e.message); // keep existing install
  } else throw e;
}

Prevention

When it happens

Trigger: Verifying a bundle that is unsigned, signed with an expired/revoked certificate, modified after signing (hash mismatch), corrupted during download/extraction, or when the codesign tool itself fails/is unavailable (empty stderr yields 'codesign unavailable').

Common situations: Re-signing or patching the app locally after build; truncated or tampered download; CI artifacts built without a signing identity; a Mac with a broken or keychain-locked codesign environment.

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


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/40bc967821413021. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/app/install-macos.mjs:33

  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 }); }
}

export function verifySignature(bundle) {
  const result=spawnSync("codesign",["--verify","--deep","--strict",bundle],{encoding:"utf8"});
  if(result.status!==0) throw new Error(`The app signature did not verify: ${result.stderr?.trim() ?? "codesign unavailable"}`);
}

export function verifyReleaseBundle(bundle) {
  verifySignature(bundle);
  const requirement='=anchor apple generic and identifier "net.codewhale.computer-use" and certificate leaf[subject.OU] = "5RDNSHA5TY"';
  for(const [command,args] of [["/usr/bin/codesign",["--verify","--strict","-R",requirement,bundle]],["/usr/sbin/spctl",["--assess","--type","execute","--verbose=2",bundle]]]) {
    const result=spawnSync(command,args,{encoding:"utf8"});
    // Gatekeeper ships with macOS. Requiring its notarized source also rejects
    // local allow-list overrides; consumer Macs do not need Xcode's stapler.
    if(result.status!==0 || (command.endsWith("/spctl") && !/^source=Notarized Developer ID\r?$/m.test(result.stderr))) throw new Error("The update is not a valid notarized Codewhale release. Your current app has been kept.");
  }
  if(!fs.existsSync(path.join(bundle,"Contents","MacOS","node"))) throw new Error("The release is missing its bundled runtime.");
}

View on GitHub (pinned to 73e0f67d83)