Hmbown/CodeWhale · error · ExecError
element does not belong to the bound application —…
Error message
element does not belong to the bound application — open_application and observe again
What it means
The backend binds input to one application (state.inputApp) established via open_application. assertBoundElement validates that the accessibility element you are targeting was observed from that same application (matching pid). If no app is bound or the element's app_ref.pid differs from the bound pid, it throws so a stale/foreign element is never driven.
Solutions
- Call open_application to (re)bind the intended application, then observe() again to get fresh element handles for it.
- Re-run observe() after any app relaunch or rebinding — old element references are stale once the pid changes.
- Check the element's app_ref.pid matches the currently bound pid before passing it to an input tool.
- In multi-app workflows, rebind explicitly each time you switch applications instead of reusing cached targets.
Example fix
// before
const el = oldTargets["Submit"]; // observed before rebinding
await click({ element: el }); // element does not belong to the bound application
// after
await open_application("com.example.app");
const fresh = await observe();
await click({ element: fresh["Submit"] }); Defensive patterns
Strategy: validation
Validate before calling
function isUsableTarget(el, boundPid) {
return el?.app_ref?.pid === boundPid;
}
// before acting:
// if (!isUsableTarget(el, state.boundPid)) await open_application(app); Try / catch
try {
await click({ element: el });
} catch (e) {
if (String(e.message).includes('does not belong to the bound application')) {
await open_application(el.app_ref);
const fresh = await observe();
await click({ element: fresh[el.label] });
} else throw e;
} Prevention
- Always observe() again after open_application; never reuse element handles across rebinds.
- Re-observe after any app relaunch (pid changes invalidate all handles).
- Track the bound pid in your automation script and compare it to each target's app_ref.pid.
- Scope element caches to a single open_application session.
When it happens
Trigger: Calling an element-targeting input tool (e.g. a11y click/type) with a target whose app_ref.pid is absent or differs from state.inputApp.pid — typically after re-binding with open_application to another app, after the bound app restarted (new pid), or when passing an element from a previous observe() session.
Common situations: Automation scripts that observe app A, then open app B, then replay old element handles; app relaunch by an updater changing the pid; caching observe() results across turns; mixing targets from screenshots taken before open_application was called.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- element has no resolved accessibility identity
- element press was not acknowledged
- menu_item_not_found
- native accessibility helper needs a built app or Xcode…
- no supported accessibility click at
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/2a19456fa989b58c.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:310
throw error;
}
const result = tryJson(r.stdout, null);
const interference = leaseVerdict(result);
if (interference !== null) result.user_input_during_lease = interference;
if (state.previewEnabled && state.inputApp && ["type", "key_event", "pointer_sequence", "bg_pointer", "bg_key", "set_value", "select_text", "perform_action", "hit_test", "click_element", "scroll_element", "focus_element"].includes(tool)) {
try { await updatePreview(); } catch (error) { result.preview_error = error.message; }
}
return result;
}
async function requireBackgroundActions() {
if ((await native("input_capabilities"))?.background_actions !== 1) {
throw Object.assign(new ExecError("Update the Computer Use helper to use background focus, selection, context menus and scrolling."), { code: "app_upgrade_required" });
}
}
function assertBoundElement(target) {
if (!state.inputApp || target.app_ref?.pid !== state.inputApp.pid) throw new ExecError("element does not belong to the bound application — open_application and observe again");
if (!Array.isArray(target.path) || !Number.isInteger(target.windowIndex) || !target.role) throw new ExecError("element has no resolved accessibility identity");
}
async function nativeLease(tool, args) {
if (tool === "pointer_sequence") requireSharedPointer();
if (!exec.runInputLease) throw new ExecError("This executor cannot safely own held input; update Computer Use");
if ((await native("input_capabilities"))?.input_lease !== 1) throw new ExecError("The native helper needs an update for disconnect-safe held input");
const helper = await nativeHelper();
try {
return await exec.runInputLease(helper, [JSON.stringify({ tool, args: { ...args, ...yieldArgs, input_app_ref: state.inputApp, foreground_input: state.foregroundInput, owner_pipe: true, input_lease: true } })]);
} catch (error) {
error.code = nativeErrorCode(error.message) ?? error.code;
throw error;
}
}
async function updatePreview(show = false) {
const win = await native("window_info", { app_ref: state.inputApp });View on GitHub (pinned to 73e0f67d83)