Hmbown/CodeWhale · error · ExecError
The native helper needs an update for selected-window text…
Error message
The native helper needs an update for selected-window text recognition
What it means
Thrown in the same include_ocr path: after confirming a valid pid, the backend queries the native helper's input_capabilities and requires window_ocr === 1, meaning the installed native helper binary supports selected-window text recognition. An older or mismatched helper lacks this capability, so the backend fails loudly instead of silently OCR-ing the wrong region. It indicates a version-skew between the JS backend and its native macOS companion.
Solutions
- Rebuild/reinstall the native macOS helper so it matches the plugin version that declares window_ocr support.
- Check the helper version: call input_capabilities directly and confirm window_ocr === 1 before retrying.
- Clear any cached helper binary and let the backend re-extract or re-launch the current one.
- As a stopgap, capture without OCR and run text recognition on the PNG yourself.
Defensive patterns
Strategy: validation
Validate before calling
const caps = await native('input_capabilities');
if (caps?.window_ocr !== 1) {
throw new Error('native helper lacks window_ocr; reinstall helper');
}
await session.capture({ app, include_ocr: true }); Type guard
const supportsWindowOcr = (caps) => caps?.window_ocr === 1;
Try / catch
try {
await session.capture({ app, include_ocr: true });
} catch (e) {
if (String(e.message).includes('native helper needs an update')) {
await reinstallNativeHelper();
return session.capture({ app, include_ocr: true });
} else throw e;
} Prevention
- Reinstall/rebuild the native helper whenever the plugin package version changes.
- Probe input_capabilities once at session start and fail fast on missing features.
- Pin plugin and helper versions together in your install scripts.
When it happens
Trigger: Any capture with include_ocr=true on macOS where the connected native helper reports input_capabilities.window_ocr !== 1 — typically an outdated helper binary, a stale cached helper, or a helper built from an older source tree.
Common situations: Upgrading the npm/plugin package without rebuilding or reinstalling the native helper; multiple helper versions on PATH; a partial build that predates the window_ocr feature flag.
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
- native helper needs an update for element identity…
- The observed application did not provide an exact process…
- application not found — call list_apps for exact names/pids
- background preview capture failed
- choose one available display for recording
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/405689844205e0d0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:959
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),
} })) };
} else {View on GitHub (pinned to 73e0f67d83)