Hmbown/CodeWhale · error · ExecError

The observed application did not provide an exact process…

Error message

The observed application did not provide an exact process identity for OCR

What it means

This error is thrown by the macOS computer-use backend in darwin.mjs when OCR of a specific application's window is requested (include_ocr) but the application resolved through the Accessibility (AX) API has no usable process ID. The backend captures only the exact application identified by pid so a changing foreground window cannot redirect the image; without a positive safe-integer pid it refuses to guess. It is a deliberate guard against OCR-ing the wrong window.

Solutions

  1. Call list_apps and pick an entry with an explicit positive numeric pid, then pass that exact pid/name reference to the capture call.
  2. Re-open or re-resolve the target with open_application so the backend gets a fresh AX identity before requesting OCR.
  3. If the app genuinely has no pid, request a plain screenshot (include_ocr=false) and run OCR on the full capture instead.

Example fix

// before
await session.capture({ app: 'Finder', include_ocr: true }); // app entry lacks pid
// after
const apps = await session.list_apps();
const finder = apps.find(a => a.name === 'Finder' && a.pid > 0);
await session.capture({ app: finder.name, pid: finder.pid, include_ocr: true });
Defensive patterns

Strategy: validation

Validate before calling

const app = await session.list_apps().then(a => a.find(x => x.name === appName));
if (!app || !Number.isSafeInteger(app.pid) || app.pid <= 0) {
  throw new Error(`cannot OCR ${appName}: no valid pid`);
}

Type guard

const hasValidPid = (app) => app != null && Number.isSafeInteger(app.pid) && app.pid > 0;

Try / catch

try {
  await session.capture({ app, include_ocr: true });
} catch (e) {
  if (String(e.message).includes('exact process identity')) {
    await session.capture({ app, include_ocr: false }); // full-frame fallback
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the screenshot/capture path with include_ocr=true after the AX lookup returned an app entry whose pid is undefined, 0, negative, or not a safe integer (checked as !Number.isSafeInteger(t.pid) || t.pid <= 0).

Common situations: Targeting a system process or app whose AX entry exposes no pid; using a name-based app reference that resolved loosely; running against sandboxed or partially accessible apps; stale AX metadata after an app restarted.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:958

      if (!Number.isSafeInteger(window_id) || window_id < 0) {
        throw Object.assign(new ExecError("set_window_frame needs window_id (a non-negative window index from list_windows)"), { code: "bad_args" });
      }
      const r = await native("set_window_frame", { app_ref, window_id, frame });
      return { ...r, verified: r?.verified === true, note: r?.note ?? "the after frame is the app's own readback; cross-check with list_windows before relying on it" };
    },
    list_windows: listWindows,
    open_application: openApplication,
    get_app_state: async ({ app_ref, detail, depth, window_id, include_ocr = false, ocr_region } = {}) => {
      const t0 = Date.now();
      const t = await native("get_app_state", { app_ref: app_ref === undefined ? state.inputApp ?? undefined : app_ref, detail, window_id });
      if (process.env.CODEWHALE_CU_DEBUG_OBSERVE) console.error(`observe ${Date.now() - t0}ms elements=${t.elements?.length} truncated=${t.truncated}`);
      if (!t.found) throw new ExecError("application not found — call list_apps for exact names/pids");
      if (include_ocr) {
        // Resolve once through AX, then capture only that exact application's
        // selected window. A changing foreground cannot redirect this image.
        let raster;
        try {
          if (!Number.isSafeInteger(t.pid) || t.pid <= 0) throw new ExecError("The observed application did not provide an exact process identity for OCR");
          if ((await native("input_capabilities"))?.window_ocr !== 1) throw new ExecError("The native helper needs an update for selected-window text recognition");
          // PNG here, against the JPEG default: this raster is fed to text
          // recognition, not to a viewer, and lossless glyph edges are what
          // Vision reads. A single window is small enough that the size the
          // JPEG default exists to solve does not arise.
          const ocrDir = path.join(recordingsDir(), "captures");
          fs.mkdirSync(ocrDir, { recursive: true });
          raster = await screenshot({
            ...(ocr_region
              ? { region: ocr_region }
              : { app_ref: { pid: t.pid, ...(t.bundle_id ? { bundle_id: t.bundle_id } : {}) }, window_id }),
            path: path.join(ocrDir, `ocr-${crypto.randomBytes(4).toString("hex")}.png`),
          });
          const ocr = await native("recognize_text", { file: raster.file });
          if (ocr?.status === "ok" && ocr.pixels?.w === raster.pixels.w && ocr.pixels?.h === raster.pixels.h && Array.isArray(ocr.blocks)) {
            t.ocr = { ...ocr, raster, blocks: ocr.blocks.map(block => ({ ...block, target: {
              type: "coordinate", x: Math.floor(block.bounds.x + block.bounds.w / 2), y: Math.floor(block.bounds.y + block.bounds.h / 2),
            } })) };

View on GitHub (pinned to 73e0f67d83)