Hmbown/CodeWhale · error · ExecError
This executor cannot safely own held input; update Computer…
Error message
This executor cannot safely own held input; update Computer Use
What it means
Held input (drags, press-and-hold sequences) must be executed by an executor that can safely own a lease — i.e. the host's Computer Use runtime exposes runInputLease so held state is released if the agent disconnects. When the executor object lacks this capability, nativeLease throws instead of leaking stuck pointer state.
Solutions
- Update the Computer Use host/executor to a version that implements runInputLease.
- If you supply a custom executor, add a runInputLease(helper, argsJson) implementation that can release the lease on disconnect/crash.
- As a stopgap, avoid held-input tools (drags / pointer_sequence) and use tap or accessibility actions that don't require a lease.
- Verify the wiring: log which executor object the backend was constructed with and confirm exec.runInputLease is a function.
Example fix
// before (custom executor)
const exec = { runInput: runInput }; // no runInputLease
// after
const exec = { runInput, runInputLease }; // lease-capable executor Defensive patterns
Strategy: try-catch
Validate before calling
if (typeof exec?.runInputLease !== 'function') {
console.warn('executor lacks input-lease support; held-input tools unavailable');
} Try / catch
try {
await pointer_sequence({ actions: drag });
} catch (e) {
if (String(e.message).includes('cannot safely own held input')) {
// fall back to non-lease actions
await click({ element: target });
} else throw e;
} Prevention
- Pin Computer Use host to a version whose executor implements runInputLease.
- Feature-detect exec.runInputLease at startup and disable drag/hold tools when absent.
- Keep custom executors in sync with the Computer Use executor contract.
- Prefer accessibility actions for drags where possible — they need no held-input lease.
When it happens
Trigger: Calling a tool that routes through nativeLease (e.g. pointer_sequence, which also triggers requireSharedPointer) on an executor built without runInputLease — an older or minimal Computer Use host/executor implementation.
Common situations: Running the darwin backend under an outdated or custom executor that predates the input-lease API; embedding the backend in a harness that wires only runInput; plugin/host version mismatch after a Computer Use update.
Related errors
- The native helper needs an update for disconnect-safe held…
- native screen recorder cannot own its client lifetime…
- 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/8a2837f373e02eef.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:316
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 });
const dir = path.join(stateDir(), "preview");
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
const temp = path.join(dir, "next.png"), file = path.join(dir, "latest.png");
const r = await runL("screencapture", ["-x", "-o", "-l", String(win.window_id), "-t", "png", temp], { timeoutMs: 8000 });
if (r.code !== 0) throw new ExecError(`background preview capture failed: ${r.stderr}`);
fs.renameSync(temp, file);View on GitHub (pinned to 73e0f67d83)