Hmbown/CodeWhale · error · ExecError
The native helper needs an update for disconnect-safe held…
Error message
The native helper needs an update for disconnect-safe held input
What it means
Even when the executor supports leases, the compiled native helper itself must advertise `input_lease: 1` in its input_capabilities for disconnect-safe held input. nativeLease queries the helper's capabilities and throws if the capability flag is missing, because older helpers would leak held pointer/keyboard state on disconnect.
Solutions
- Delete the stale cached helper: rm ~/.codewhale-cu/bin/accessibility-* so the backend recompiles from current source.
- Update Computer Use so a fresh helper with input_lease support is built/bundled.
- Verify capabilities manually by running the helper's input_capabilities and checking input_lease === 1.
- Avoid held-input tools until the helper is updated; use tap/accessibility actions instead.
Example fix
// before (shell)
# old cached helper reused
pointer_sequence({ hold: ... }) // helper needs an update
// after (shell, one-time)
rm -f ~/.codewhale-cu/bin/accessibility-*
pointer_sequence({ hold: ... }) Defensive patterns
Strategy: validation
Validate before calling
const caps = await native('input_capabilities');
if (caps?.input_lease !== 1) {
throw new Error('refresh helper cache: rm ~/.codewhale-cu/bin/accessibility-*');
} Try / catch
try {
await pointer_sequence(args);
} catch (e) {
if (String(e.message).includes('needs an update for disconnect-safe held input')) {
throw new Error('delete cached native helper (~/.codewhale-cu/bin) and restart to rebuild');
}
throw e;
} Prevention
- Clear the ~/.codewhale-cu/bin helper cache after every Computer Use upgrade.
- Check input_capabilities.input_lease at session start before offering held-input tools.
- Don't ship a bundled pre-built helper older than the backend source — let it rebuild.
- Gate drag/hold features on a capability probe rather than assuming support.
When it happens
Trigger: nativeLease runs `native("input_capabilities")` and the returned object's input_lease is not 1 — the cached native helper at ~/.codewhale-cu/bin/accessibility-<hash> was compiled from an older helper source and is being reused after the backend was updated.
Common situations: A stale cached helper compiled before the input-lease feature was added; the app bundles an outdated helper so the clang-rebuild path (which would compile fresh source) is skipped; failed cache invalidation after a Computer Use upgrade.
Related errors
- This executor cannot safely own held input; update Computer…
- 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/eae74ed5e4b387ea.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:317
}
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);
const p = state.pointer;View on GitHub (pinned to 73e0f67d83)