Hmbown/CodeWhale · error · Error

The practice window screenshot could not be verified. Check…

Error message

The practice window screenshot could not be verified. Check Screen Recording permission.

What it means

The final verification screenshots the practice window and asserts the capture is attributable and non-empty: capture.app_ref.pid must equal the practice child's pid and pixel dimensions must be present. When they are not, the screenshot pipeline could not actually capture the window — on macOS this is the signature of missing Screen Recording (TCC) permission, so the capture returns empty or falls back to something else.

Solutions

  1. Grant Screen Recording permission (System Settings > Privacy & Security > Screen Recording) to the process running the check, then restart it and retry.
  2. Confirm the practice app is still running at screenshot time (no early exit).
  3. If permission is granted and it still fails, inspect the screenshot tool's returned app_ref/pixels in logs and file a computer-use plugin issue.

Example fix

// before
await runBackgroundCheck({ bundle }); // Screen Recording not granted

// after
// Grant Screen Recording to the host process first, restart it, then:
try {
  await runBackgroundCheck({ bundle });
} catch (e) {
  if (String(e.message).includes("Screen Recording")) {
    console.error("Grant Screen Recording permission and retry.");
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await runBackgroundCheck({ bundle });
} catch (e) {
  if (String(e.message).includes("Screen Recording")) {
    console.error("Grant Screen Recording permission (System Settings > Privacy & Security), restart the process, retry.");
  }
  throw e;
}

Prevention

When it happens

Trigger: In runBackgroundCheck, the screenshot tool reply has capture.app_ref?.pid !== child.pid or capture.pixels?.w/h falsy, i.e. a zero-sized, empty, or wrong-window capture.

Common situations: Screen Recording permission never granted (or granted to the wrong process), captures silently returning blank frames without permission, or the practice window closing before the screenshot.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

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

    }
    const entry = state.elements.find(element => element.role === "AXTextField" && element.label === "Practice text");
    const apply = state.elements.find(element => element.role === "AXButton" && element.label === "Apply");
    if (!entry || !apply) throw new Error("The practice controls could not be read. Check Accessibility permission and retry.");
    // Backend targets are resolved AX records, using the exact state index.
    const focus = await call("resolve_element", { app_ref: { pid: child.pid }, windowIndex: entry.windowIndex, path: entry.path });
    if (!focus.found) throw new Error("The practice text field changed. Run the check again.");
    await call("left_click", { target: { ...focus.element, type: "element", app_ref: { pid: child.pid } } });
    const phrase = "Background check complete 🐋";
    await call("type", { text: phrase });
    if (demoDirectory) await call("screenshot", { app_ref: { pid: child.pid }, path: path.join(demoDirectory, "02-entered.png") });
    const button = await call("resolve_element", { app_ref: { pid: child.pid }, windowIndex: apply.windowIndex, path: apply.path });
    if (!button.found) throw new Error("The practice Apply button changed. Run the check again.");
    await call("left_click", { target: { ...button.element, type: "element", app_ref: { pid: child.pid } } });
    await until(() => applied);
    if (applied.value !== phrase) throw new Error("The text received by the practice window did not match. Check the app log and retry.");
    const capture = await call("screenshot", { app_ref: { pid: child.pid }, path: path.join(scratch, "practice.png") });
    if (demoDirectory) fs.copyFileSync(capture.file, path.join(demoDirectory, "03-verified.png"));
    if (capture.app_ref?.pid !== child.pid || !capture.pixels?.w || !capture.pixels?.h) throw new Error("The practice window screenshot could not be verified. Check Screen Recording permission.");
    const count = latest.samples;
    await until(() => latest.samples > count);
    const isolated = latest.samples > 0 && latest.pointerChanges === 0 && latest.foregroundChanges === 0;
    return { ok: isolated, edited: true, screenshot: true, samples: latest.samples, pointerChanges: latest.pointerChanges, foregroundChanges: latest.foregroundChanges,
      message: isolated ? "Text entered, Apply verified and the practice window captured. Your foreground app and pointer stayed unchanged."
        : "Edit and screenshot verified. Your app or pointer moved during the check, so background isolation is inconclusive." };
  } finally {
    clearTimeout(timer);
    try { await closeSession(sessionId); }
    finally {
      if (child.exitCode === null && child.signalCode === null) child.kill("SIGTERM");
      fs.rmSync(scratch, { recursive: true, force: true });
    }
  }
}

View on GitHub (pinned to 73e0f67d83)