Hmbown/CodeWhale · error · ExecError
coordinates must be finite numbers
Error message
coordinates must be finite numbers
What it means
assertInScreen guards every pointer/coordinate action: x and y must both be finite numbers (screen coordinates). NaN, Infinity, or non-numeric coordinates would otherwise be forwarded to the native helper and produce undefined clicks at arbitrary positions, so they are rejected up front.
Solutions
- Parse coordinates with Number() and validate Number.isFinite(x) && Number.isFinite(y) before calling the tool.
- Take a fresh screenshot/observe to get real coordinates instead of reusing ones derived from stale or failed data.
- Fix scaling math: guard every multiplication/division that derives x/y from image-vs-screen scale factors.
- If arguments come from a model, tighten the tool schema so x/y are required finite numbers.
Example fix
// before
const x = annot.left * scale; // annot.left undefined → NaN
await click({ x, y });
// after
const x = Number(annot?.left) * scale, y = Number(annot?.top) * scale;
if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error("bad point");
await click({ x, y }); Defensive patterns
Strategy: type-guard
Validate before calling
function isPoint(p) { return Number.isFinite(p?.x) && Number.isFinite(p?.y); }
if (!isPoint(pt)) throw new Error('coordinates must be finite numbers before calling the tool'); Type guard
function isFinitePoint(p) {
return typeof p === 'object' && p !== null && Number.isFinite(p.x) && Number.isFinite(p.y);
} Try / catch
try {
await click({ x, y });
} catch (e) {
if (String(e.message).includes('coordinates must be finite')) {
const shot = await screenshot(); // re-derive coordinates from fresh data
({ x, y } = toScreenPoint(shot));
await click({ x, y });
} else throw e;
} Prevention
- Run Number.isFinite on every coordinate at the boundary where arguments enter your automation code.
- Avoid undefined-propagation in scale math: default and validate every scale factor.
- Convert model-emitted string coordinates with Number() and reject NaN early.
- Enforce a strict JSON schema (type: number) for x/y tool arguments.
When it happens
Trigger: Calling a coordinate-based tool (click, move, scroll, drag endpoints) with x or y that is NaN, ±Infinity, undefined, null, or a non-number — usually from parsing screenshot annotations into bad numbers or dividing by zero when scaling coordinates.
Common situations: LLM-emitted arguments with missing/garbled coordinates; coordinate scaling math producing NaN (e.g. multiplying undefined); JSON payloads where coordinates arrive as strings like "120" that later math turns into NaN; empty screenshot annotations yielding undefined points.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- bad_target
- coordinates must be finite numbers
- Automation name is required
- Automation prompt is required
- invalid draft id
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/83cc94236a41bbd3.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:361
// Any successful capture (bind, explicit preview, action refresh) starts
// the live refresh; the tick itself re-enters this function as a no-op.
if (state.previewEnabled && state.inputApp) startPreviewLoop();
return { enabled: true, file, app: state.inputApp, pointer: p };
}
// ---------- pointer input ----------
// Our qualified raw pointer path uses the shared event tap, which moves
// the user's real cursor. Process/window-directed mouse delivery has not
// passed the independent fixture. So the pointer path is:
// 1. accessibility action on the element under the point (quiet, exact),
// 2. otherwise refuse in background mode. Explicit foreground control
// permits a global gesture only when the bound application owns the
// window under the point. Restoring the cursor is not isolation.
// Every receipt says which of the two happened.
function mouseName(button) { return { left: "left", right: "right", middle: "middle" }[button] ?? "left"; }
function assertInScreen(x, y) {
if (!Number.isFinite(x) || !Number.isFinite(y)) throw new ExecError("coordinates must be finite numbers");
}
function buttonCode(button) { return button === "middle" ? 2 : button === "right" ? 1 : 0; }
function requireSharedPointer() {
if (!state.foregroundInput) throw Object.assign(new ExecError("This action needs the shared macOS pointer and was not sent in background mode. Use an accessibility action or a separate computer; foreground control requires exclusive desktop use authorized by the user."), { code: "shared_pointer_required" });
}
/** Refuse a global gesture whose landing point belongs to another application. */
async function assertOwnsPoint(x, y) {
if (!state.inputApp) throw new ExecError("open_application first to choose which application receives input");
const w = await native("window_at_point", { x, y });
if (!w?.found) throw new ExecError(`no window at (${x}, ${y}) — take a fresh screenshot and choose a point inside the target window`);
if (w.owner_pid !== state.inputApp.pid) {
throw new ExecError(`(${x}, ${y}) is covered by a window owned by ${w.owner_name || "another application"} (pid ${w.owner_pid}) — use an accessibility element target or a separate computer; no pointer input was sent`);
}
return w;
}View on GitHub (pinned to 73e0f67d83)