Hmbown/CodeWhale · critical · Error

The release is missing its bundled runtime.

Error message

The release is missing its bundled runtime.

What it means

After signature and notarization checks, verifyReleaseBundle confirms the bundle ships its bundled Node runtime at Contents/MacOS/node. A notarized but incomplete bundle — validly signed yet missing the runtime the daemon needs — is rejected so a half-published release can never replace a working install.

Solutions

  1. Rebuild the release bundle ensuring node is copied into Contents/MacOS before signing and notarization
  2. Re-download the official release and re-verify (the current copy may be truncated)
  3. Check validateReleaseZip/extraction logic if a local extraction dropped the file
  4. Inspect the staged bundle with ls Contents/MacOS to confirm what is actually present

Example fix

// before (bundle staged without runtime)
code.verifyReleaseBundle(staged); // throws: missing bundled runtime
// after
code.fs.mkdirSync(path.join(staged, "Contents", "MacOS"), { recursive: true });
code.fs.copyFileSync(path.join(runtimeDir, "node"), path.join(staged, "Contents", "MacOS", "node"));
code.verifyReleaseBundle(staged);
Defensive patterns

Strategy: validation

Validate before calling

if (!fs.existsSync(path.join(bundle, "Contents", "MacOS", "node"))) {
  throw new Error("bundle missing bundled node runtime");
}

Try / catch

try {
  verifyReleaseBundle(bundle);
} catch (e) {
  if (e.message.includes("missing its bundled runtime")) {
    discardStagedBundle(bundle); // fail closed, keep current install
  } else throw e;
}

Prevention

When it happens

Trigger: Verifying a release bundle whose Contents/MacOS/node is absent: a packaging step skipped the runtime, an extraction dropped it, or a manually assembled bundle was signed without including node.

Common situations: Release packaging regression where the runtime copy step was removed or its path changed; a ZIP validation/extraction bug silently omitting an entry; someone rebuilt the bundle by hand and forgot the node binary.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

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

    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)