Hmbown/CodeWhale · error · ExecError
no agent pointer position — mouse_move or left_mouse_down…
Error message
no agent pointer position — mouse_move or left_mouse_down first
What it means
left_mouse_up needs a location to release at: it uses the explicit target, else falls back to state.pointer (the last agent-positioned point). If neither exists — no mouse_move or left_mouse_down was performed in this session — it throws this error rather than guessing a coordinate.
Solutions
- Pass an explicit target location: left_mouse_up({ x, y }) with coordinates inside the screen bounds.
- Call mouse_move({ x, y }) first so state.pointer is populated, then left_mouse_up() without arguments.
- Re-run left_mouse_down in this session before releasing, which also sets state.pointer.
Example fix
// before: release with no position established in this session
await left_mouse_up(); // throws
// after: position first, then release
await mouse_move({ x: 300, y: 400 });
await left_mouse_up(); Defensive patterns
Strategy: validation
Validate before calling
function assertMouseUpReady(loc) {
const ok = (loc && Number.isFinite(loc.x) && Number.isFinite(loc.y)) || lastKnownPointer != null;
if (!ok) throw new Error('mouse_up needs a target or prior mouse_move/left_mouse_down');
} Type guard
function hasPointerPosition(loc) {
return loc != null && Number.isFinite(loc.x) && Number.isFinite(loc.y);
} Try / catch
try {
await left_mouse_up();
} catch (e) {
if (String(e.message).includes('no agent pointer position')) {
await mouse_move({ x: lastKnownPointer.x, y: lastKnownPointer.y });
await left_mouse_up();
} else throw e;
} Prevention
- Always run mouse_move (or left_mouse_down) before left_mouse_up so the session has a pointer position.
- Pass explicit {x, y} coordinates to left_mouse_up when you know the release point — do not rely on session state.
- After recreating the session, re-establish pointer position before any release.
When it happens
Trigger: Calling left_mouse_up with no target argument when the session never ran mouse_move or left_mouse_down, so state.pointer is undefined and there is no location to release the button at.
Common situations: A fresh session restores only the lease (or the caller assumes pointer state persists across sessions); an agent skips the positioning step before drag-release; a caller passes a malformed target (missing x/y) and it is ignored.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- no agent pointer button is held by this session
- this session already holds the left pointer button; release…
- no window at ( , ) — take a fresh screenshot and choose a…
- open_application first to choose which application receives…
- shared_pointer_required
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/901a042f47143555.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:1057
const r = await gesture([{ type: MOUSE_MOVED, x: target.x, y: target.y, button: 0, clickState: 0 }], { restore: false, guard: target });
return { action_sent: true, strategy: "event", at: { x: target.x, y: target.y }, ...pointerCost(r) };
},
left_mouse_down: async ({ target } = {}) => {
assertInScreen(target?.x, target?.y);
requireSharedPointer();
if (state.pointerLease) throw new ExecError("this session already holds the left pointer button; release it first");
await assertOwnsPoint(target.x, target.y);
state.pointerLease = await nativeLease("pointer_sequence", { steps: [
{ type: MOUSE_MOVED, x: target.x, y: target.y, button: 0, clickState: 0 },
{ type: MOUSE.left.down, x: target.x, y: target.y, button: 0, clickState: 1 },
], restore: false });
state.pointer = { x: target.x, y: target.y };
return { action_sent: true, strategy: "event", at: state.pointer, ...pointerCost(state.pointerLease.receipt) };
},
left_mouse_up: async ({ target } = {}) => {
if (!state.pointerLease) throw new ExecError("no agent pointer button is held by this session");
const loc = target ?? state.pointer;
if (!loc) throw new ExecError("no agent pointer position — mouse_move or left_mouse_down first");
assertInScreen(loc.x, loc.y);
// No ownership guard: the button is already held, and the drag may have
// legitimately left the originating window.
try { await withSignal(null, () => state.pointerLease.release({ point: loc })); }
finally { state.pointerLease = null; }
state.pointer = { x: loc.x, y: loc.y };
return { action_sent: true, strategy: "event", at: state.pointer, pointer_moved: true, pointer_restored: false };
},
left_click_drag: async ({ from_target: from, to } = {}) => {
assertInScreen(from?.x, from?.y); assertInScreen(to?.x, to?.y);
const steps = [
{ type: MOUSE_MOVED, x: from.x, y: from.y, button: 0, clickState: 0 },
{ type: MOUSE.left.down, x: from.x, y: from.y, button: 0, clickState: 1, delayMs: 60 },
];
const n = 12;
for (let i = 1; i <= n; i++) {
steps.push({ type: MOUSE.left.dragged, x: from.x + ((to.x - from.x) * i) / n, y: from.y + ((to.y - from.y) * i) / n, button: 0, clickState: 1, delayMs: 45 });
}View on GitHub (pinned to 73e0f67d83)