Hmbown/CodeWhale · error · Error
The background check requires the installed macOS app.
Error message
The background check requires the installed macOS app.
What it means
runBackgroundCheck in the computer-use plugin's background-check.mjs refuses to run unless the host is macOS and a bundle path for the installed Computer Use app was supplied. The check drives a practice app inside the installed .app bundle, so without it there is nothing to launch. This is a precondition guard at the top of the function, raised before any process is spawned.
Solutions
- Install the Computer Use macOS app and pass its bundle path: runBackgroundCheck({ bundle: '/Applications/Computer Use.app' }).
- On non-macOS hosts, skip the background check or route it to a macOS machine; the check cannot run there.
- Resolve the bundle programmatically (e.g. via mdfind or a known install path) instead of hardcoding, so updates to the install location do not break the call.
Example fix
// before
await runBackgroundCheck(); // fails: no bundle, maybe not macOS
// after
if (process.platform !== "darwin") throw new Error("Background check needs macOS.");
await runBackgroundCheck({ bundle: "/Applications/Computer Use.app" }); Defensive patterns
Strategy: validation
Validate before calling
if (process.platform !== "darwin") throw new Error("Background check needs macOS.");
if (!bundle) throw new Error("Pass the installed Computer Use app bundle path."); Type guard
function canRunBackgroundCheck(opts) {
return process.platform === "darwin" && typeof opts?.bundle === "string" && opts.bundle.length > 0;
} Try / catch
try {
await runBackgroundCheck({ bundle });
} catch (e) {
if (String(e.message).includes("requires the installed macOS app")) {
throw new Error("Run this on macOS with the Computer Use app installed and pass { bundle }.");
}
throw e;
} Prevention
- Gate the check behind process.platform === 'darwin' in your setup code.
- Resolve and pass the bundle path in one shared setup helper, never inline ad hoc.
- Document the macOS + installed-app prerequisite wherever the check is invoked.
When it happens
Trigger: Calling runBackgroundCheck() when process.platform !== 'darwin', or calling it on macOS without passing { bundle } (or passing bundle: null/undefined).
Common situations: Running the check on Linux/CI (non-macOS), invoking it from a dev environment where the app was never installed, or a caller that forgot to resolve and pass the installed app's bundle path.
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
- open failed
- app_denied
- application not found — call list_apps for exact names/pids
- background preview capture failed
- cannot discard a loaded Runtime thread
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/73e3889bd47646b7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/app/background-check.mjs:11
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import crypto from "node:crypto";
import { spawn } from "node:child_process";
import { setTimeout as delay } from "node:timers/promises";
import { handle, closeSession } from "../src/app-handler.mjs";
/** Uses the same daemon backend and cancellation path as connected hosts. */
export async function runBackgroundCheck({ bundle, demoDirectory } = {}) {
if (process.platform !== "darwin" || !bundle) throw new Error("The background check requires the installed macOS app.");
const executable = path.join(bundle, "Contents", "Resources", "Practice.app", "Contents", "MacOS", "practice");
if (!fs.existsSync(executable)) throw new Error("Update the Computer Use app to run the background check.");
const sessionId = `setup-${crypto.randomUUID()}`;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 15_000);
const child = spawn(executable, [], { stdio: ["ignore", "pipe", "pipe"] });
const scratch = fs.mkdtempSync(path.join(os.tmpdir(), "cu-setup-capture-"));
let ready = false, applied = null, latest = null, buffer = "", spawnError = null;
child.on("error", error => { spawnError = error; });
child.stderr.on("data", () => {});
child.stdout.setEncoding("utf8");
child.stdout.on("data", chunk => {
buffer += chunk;
if (buffer.length > 16_384) { controller.abort(); return; }
let newline;
while ((newline = buffer.indexOf("\n")) >= 0) {
const line = buffer.slice(0, newline); buffer = buffer.slice(newline + 1);
try { const result = JSON.parse(line); latest = result; if (result.event === "ready") ready = true; if (result.event === "applied") applied = result; } catch { /* Cocoa diagnostics are not receipts. */ }View on GitHub (pinned to 73e0f67d83)