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
- Call list_apps and pick an entry with an explicit positive numeric pid, then pass that exact pid/name reference to the capture call.
- Re-open or re-resolve the target with open_application so the backend gets a fresh AX identity before requesting OCR.
- 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
- Always resolve targets through list_apps and require a positive pid before OCR calls.
- Re-resolve the app after it restarts; AX identities go stale.
- Treat system/daemon processes as non-OCR-able targets.
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
- element does not belong to the bound application —…
- element has no resolved accessibility identity
- element press was not acknowledged
- menu_item_not_found
- native accessibility helper needs a built app or Xcode…
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)