Hmbown/CodeWhale · critical · ExecError
native screen recorder cannot own its client lifetime…
Error message
native screen recorder cannot own its client lifetime; update Computer Use before recording
What it means
Before spawning the native helper recorder, the backend queries input_capabilities and requires record_owner_pipe === 1, i.e. the installed native Computer Use component supports handing recorder client-lifetime ownership through a pipe. Older helper builds lack this handshake, so recording is refused with an upgrade instruction rather than risking orphaned or mis-owned recorder processes.
Solutions
- Update the Computer Use native component to the version matching this backend, then retry.
- Verify with the helper's input_capabilities that record_owner_pipe is 1.
- If update is not possible, avoid app/window record on this machine or capture with screenshot instead.
- Check that no stale older helper binary is earlier on PATH / in the plugin's helper lookup location.
Defensive patterns
Strategy: try-catch
Try / catch
try { await record({ app_ref }) } catch (e) {
if (String(e.message).includes("cannot own its client lifetime")) {
// native helper is outdated; surface an upgrade instruction to the user
console.error("Update the Computer Use native component before recording.");
} else throw e;
} Prevention
- Keep the native Computer Use helper and the JS backend versions in lockstep; update both together.
- Check input_capabilities().record_owner_pipe === 1 before offering record actions.
- Avoid record calls on machines with known-stale helper installs; use screenshot instead.
When it happens
Trigger: Running the darwin backend against an outdated native Computer Use helper binary whose input_capabilities reports no record_owner_pipe support; a partially updated install where the JS backend is newer than the bundled native helper.
Common situations: Upgrading the TUI/plugin but not the native helper; a stale helper cached at ~/Library or another install location; rolling back the plugin while keeping a newer/older helper.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- choose one available display for recording
- durationSec must be positive
- The native helper needs an update for disconnect-safe held…
- the selected application has no capturable window — call…
- This executor cannot safely own held input; update Computer…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/87ebb68afa482d34.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:639
// it does not track later moves or resizes.
let window = null;
if (app_ref !== undefined || window_id != null) {
window = await native("window_info", { app_ref: app_ref === undefined ? state.inputApp ?? undefined : app_ref, window_id });
if (!window?.points || !(window.points.w > 0) || !(window.points.h > 0)) throw new ExecError("the selected application has no capturable window — call list_windows");
if (region) throw new ExecError("choose app_ref or region, not both");
region = [window.points.x, window.points.y, window.points.w, window.points.h];
}
let disp = display ?? state.activeDisplay;
if (window && display == null) {
const cx = region[0] + region[2] / 2, cy = region[1] + region[3] / 2;
const host = displays.find(d => d.points && cx >= d.points.x && cx < d.points.x + d.points.w && cy >= d.points.y && cy < d.points.y + d.points.h);
if (host) disp = host.index;
}
const selected = displays.find(d => d.index === disp);
if (!selected) throw new ExecError("choose one available display for recording");
if (durationSec != null && (!Number.isFinite(durationSec) || durationSec <= 0)) throw new ExecError("durationSec must be positive");
const capabilities = await native("input_capabilities");
if (capabilities?.record_owner_pipe !== 1) throw new ExecError("native screen recorder cannot own its client lifetime; update Computer Use before recording");
const helper = await nativeHelper();
throwIfAborted();
const child = spawn(helper, [JSON.stringify({ tool: "record", args: { file, displayID: selected.id, region, durationSec, owner_pipe: true } })], { stdio: ["pipe", "pipe", "pipe"] });
child.stdin.on("error", () => {});
const startedAt = new Date().toISOString();
let stderr = "", output = "", ready = false;
const completion = new Promise(resolve => {
child.once("error", error => resolve({ code: -1, error: error.message }));
child.once("close", code => resolve({ code, error: stderr.trim() }));
});
child.stderr.on("data", chunk => { stderr = (stderr + chunk).slice(-4000); });
const recording = { child, completion, pid: child.pid, file, startedAt, mode: "ScreenCaptureKit", display: disp };
rec.set(id, recording);
const signal = currentSignal();
let timer, abort;
try {
await new Promise((resolve, reject) => {
abort = () => { requestRecordingStop(recording); reject(Object.assign(new ExecError("computer request cancelled"), { code: "cancelled" })); };View on GitHub (pinned to 73e0f67d83)