Hmbown/CodeWhale · error · ExecError
the selected application has no capturable window — call…
Error message
the selected application has no capturable window — call list_windows
What it means
Before recording a specific application's window, the record action resolves the app via native window_info. If the resolved window has no points or a zero-width/height rect, there is nothing capturable to record and the library throws, directing the caller to list_windows.
Solutions
- Call list_windows to get currently capturable windows and use a valid app_ref or window_id from it.
- Un-minimize the target app or bring it on-screen so it has a non-zero window rect.
- Record a display or region instead of an app window if the app has no GUI window.
- Re-resolve app_ref after the target application was restarted.
Example fix
// before
await record({ app_ref: staleRef });
// after
const wins = await list_windows();
await record({ app_ref: wins[0].app_ref }); Defensive patterns
Strategy: fallback
Validate before calling
const wins = await list_windows();
const target = wins.find(w => w.app_ref === appRef || w.window_id === windowId);
if (!target || !target.points || !(target.points.w > 0) || !(target.points.h > 0))
throw new Error("target app has no capturable window"); Type guard
const isCapturable = (w) => w?.points && w.points.w > 0 && w.points.h > 0;
Try / catch
try { await record({ app_ref }) } catch (e) {
if (String(e.message).includes("no capturable window")) {
const wins = await list_windows();
if (wins.length) await record({ app_ref: wins[0].app_ref });
} else throw e;
} Prevention
- Resolve app_ref/window_id fresh from list_windows before each recording.
- Un-minimize the target app; minimized and menu-bar-only apps have no capturable window.
- Fall back to display/region recording when no app window qualifies.
When it happens
Trigger: Calling record({ app_ref }) for an app with no on-screen windows (all minimized or only background windows); passing a window_id/app_ref that no longer exists; the app has a window whose rect reports zero size; stale app_ref after the target app quit.
Common situations: Target app is minimized to the Dock; targeting helper/background apps (menu-bar-only apps have no capturable window); the app crashed or was relaunched so the cached app_ref is stale.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- choose one available display for recording
- durationSec must be positive
- native screen recorder cannot own its client lifetime…
- application not found — call list_apps for exact names/pids
- application window not found in UIA tree — pass…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/108ce85d13a94a03.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:625
}, timeoutMs);
})]);
} finally { clearTimeout(timer); }
}
async function recordingStart({ display, durationSec, region, app_ref, window_id } = {}) {
const dir = recordingsDir();
fs.mkdirSync(dir, { recursive: true });
const id = crypto.randomBytes(4).toString("hex");
const file = path.join(dir, `rec-${id}.mov`);
const displays = await displayInfo();
// app_ref scopes the recording to the app's window rect: resolved once at
// start through the same window_info the AX path uses, so a background
// window records behind the user's work. The rect is fixed at start —
// 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", () => {});View on GitHub (pinned to 73e0f67d83)