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

  1. Pass an explicit target location: left_mouse_up({ x, y }) with coordinates inside the screen bounds.
  2. Call mouse_move({ x, y }) first so state.pointer is populated, then left_mouse_up() without arguments.
  3. 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

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


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)