Hmbown/CodeWhale · error · ExecError
coordinates must be finite numbers
Error message
coordinates must be finite numbers
What it means
clickAt validates its x/y coordinates with Number.isFinite before building the PowerShell mouse_event script. Non-finite coordinates (NaN, Infinity, undefined, non-numeric strings that coerce to NaN) throw this error immediately, before any input is injected. It is a pure input-validation guard protecting Windows' native input path.
Solutions
- Validate coordinates before clicking: ensure both are finite numbers (Number.isFinite(Number(x)) mirrors the check).
- Fix the coordinate source — check that the detection step actually produced a match before dispatching a click.
- Correct key names when destructuring (e.g. { x, y } vs { left, top }) so the right fields are passed.
- Clamp or reject out-of-range/scaled values before they reach the backend.
Example fix
// before
await backend.left_click({ target: { x: match?.cx, y: match?.cy } });
// after
const x = Number(match?.cx), y = Number(match?.cy);
if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error('no click target resolved');
await backend.left_click({ target: { x, y } }); Defensive patterns
Strategy: validation
Validate before calling
function assertClickablePoint(p) {
const x = Number(p?.x), y = Number(p?.y);
if (!Number.isFinite(x) || !Number.isFinite(y))
throw new Error(`click target unresolved: ${JSON.stringify(p)}`);
return { x, y };
} Type guard
const isFinitePoint = (p) => p != null && Number.isFinite(Number(p.x)) && Number.isFinite(Number(p.y));
Try / catch
try {
await backend.left_click({ target: point });
} catch (e) {
if (e.message === 'coordinates must be finite numbers') {
console.warn('detection returned no point; skipping click', point);
return null;
}
throw e;
} Prevention
- Validate detection output (x/y present and finite) before every click.
- Check for null matches from vision/OCR steps instead of destructuring undefined.
- Guard scaling math against divide-by-zero producing Infinity.
- Keep coordinate keys consistent ({x,y} vs {left,top}) across your pipeline.
When it happens
Trigger: Calling clickAt (directly or via left_click/double_click/right_click) with target.x or target.y that is undefined, null, NaN, Infinity, or a non-numeric string — typically from a malformed detection result or a JSON payload missing coordinates.
Common situations: An upstream vision/OCR step returned no match so x/y were never assigned; destructuring a click point with the wrong key names; screen-coordinate scaling math dividing by zero to produce Infinity.
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
- coordinates must be finite numbers
- unknown key combination
- Automation name is required
- Automation prompt is required
- bad_target
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/a98134423d128834.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/win32.mjs:594
recordingStart: async () => {
throw Object.assign(new ExecError("Recording is unavailable on this platform until the recorder has session-owned cleanup. Use screenshots instead."), { code: "owned_recording_unavailable" });
},
recordingStop: async ({ id }) => { throw new ExecError(`unknown recording "${id}"`); },
recordingStatus: ({ id }) => ({ id, running: false }),
recordingList: async () => {
const dir = recordingsDir();
const out = fs.existsSync(dir)
? fs.readdirSync(dir).filter((f) => /\.mp4$/i.test(f)).map((f) => {
const st = fs.statSync(path.join(dir, f));
return { file: path.join(dir, f), bytes: st.size, modifiedAt: st.mtime.toISOString() };
}).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, 50)
: [];
return { dir, recordings: out, running: [] };
},
};
async function clickAt(button, x, y, clicks) {
if (!Number.isFinite(Number(x)) || !Number.isFinite(Number(y))) throw new ExecError("coordinates must be finite numbers");
const flags = button === 1 ? "RIGHTDOWN, RIGHTUP" : button === 2 ? "MIDDLEDOWN, MIDDLEUP" : "LEFTDOWN, LEFTUP";
const seq = [];
for (let i = 0; i < clicks; i++) seq.push(`[User32]::mouse_event([User32]::${flags.split(",")[0].trim()}, 0, 0, 0, [UIntPtr]::Zero); Start-Sleep -Milliseconds 40; [User32]::mouse_event([User32]::${flags.split(",")[1].trim()}, 0, 0, 0, [UIntPtr]::Zero); Start-Sleep -Milliseconds 60;`);
const held = button === 1 ? "RIGHT" : button === 2 ? "MIDDLE" : "LEFT";
throwIfAborted();
heldButtons.add(held);
try {
await withUser32(`[User32]::SetCursorPos(${Math.round(x)}, ${Math.round(y)}) | Out-Null;
Start-Sleep -Milliseconds 60;
${seq.join("\n")}
Write-Output '{"ok": true}';`, { timeoutMs: 20_000 });
heldButtons.delete(held);
return { action_sent: true, at: { x: Number(x), y: Number(y) }, button, clicks };
} finally { await releaseInput({ buttons: [held], keys: [] }); }
}
}
export default { create };View on GitHub (pinned to 73e0f67d83)