Hmbown/CodeWhale · error · Error

${reply.error.message}

Error message

${reply.error.message}

What it means

call() is the thin wrapper the background check uses around the app-handler's handle() for every tool invocation (open_application, get_app_state, type, screenshot, ...). When the handler replies with reply.ok === false, the reply's error message is rethrown verbatim. The real cause is whatever the underlying tool reported — permission denials, invalid targets, internal tool failures.

Solutions

  1. Read the rethrown message — it is the underlying tool's own error — and fix the cause it names (usually a macOS permission or an invalid target).
  2. Re-run the check after granting Accessibility/Screen Recording permissions; macOS only applies them to already-running processes after approval.
  3. If the inner message indicates a tool bug, capture the sessionId and app logs and file an issue against the computer-use plugin.

Example fix

// before
await runBackgroundCheck({ bundle }); // opaque propagation

// after
try {
  await runBackgroundCheck({ bundle });
} catch (e) {
  // e.message is the raw tool error, e.g. a TCC permission denial
  console.error("Background check tool failure:", e.message);
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await runBackgroundCheck({ bundle });
} catch (e) {
  // e.message is the underlying tool's own error — log it with context
  console.error(`Background check failed at a tool call: ${e.message}`);
  if (/permission|Accessibility|Screen Recording/i.test(e.message)) {
    console.error("Grant the named macOS permission, restart the process, retry.");
  }
  throw e;
}

Prevention

When it happens

Trigger: Any call(tool, args) in runBackgroundCheck where handle({ tool, args }, { sessionId, signal, persistentInputOwner: true }) resolves with { ok: false, error: { message } }; the thrown Error's message equals that inner message.

Common situations: Accessibility or Screen Recording permissions revoked mid-run, a tool receiving stale AX coordinates, or the daemon backend returning an internal error during a step.

Related errors


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

Appendix: source

Thrown at crates/tui/plugins/computer-use/app/background-check.mjs:41

  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. */ }
    }
  });
  async function until(predicate) {
    while (!predicate()) {
      if (spawnError) throw spawnError;
      if (controller.signal.aborted || child.exitCode !== null) throw new Error("The check was interrupted. You can run it again when ready.");
      await delay(40);
    }
  }
  async function call(tool, args = {}) {
    const reply = await handle({ tool, args }, { sessionId, signal: controller.signal, persistentInputOwner: true });
    if (!reply.ok) throw new Error(reply.error.message);
    return reply.data;
  }
  try {
    await until(() => ready);
    await call("open_application", { pid: child.pid, activate: false });
    const state = await call("get_app_state");
    if (demoDirectory) {
      fs.mkdirSync(demoDirectory, { recursive: true });
      // AX can register before WindowServer makes a new window capturable.
      // Retry only this read, before sending any input, within the check limit.
      while (true) {
        try { await call("screenshot", { app_ref: { pid: child.pid }, path: path.join(demoDirectory, "01-ready.png") }); break; }
        catch (error) {
          if (controller.signal.aborted || !error.message.includes("not capturable")) throw error;
          await delay(100, undefined, { signal: controller.signal });
        }
      }
    }

View on GitHub (pinned to 73e0f67d83)