Hmbown/CodeWhale · error

pointer action must be "move", "down" or "up

Error message

pointer action must be "move", "down" or "up" (got ${JSON.stringify(args.action)})

What it means

The `pointer` tool maps its `action` argument to a wire tool: move, down, or up. When args.action is anything else (or undefined), the lookup returns undefined and this error is thrown because the resolver cannot translate the request.

Solutions

  1. Set action to exactly "move", "down", or "up"
  2. Use the dedicated click tool instead of pointer for click semantics
  3. Check that the action key is spelled and cased correctly and actually present in args

Example fix

// before
resolveTool("pointer", { action: "click" })
// after
resolveTool("pointer", { action: "move" }) // or "down" / "up"; use "click" tool for clicks
Defensive patterns

Strategy: validation

Validate before calling

if (!['move','down','up'].includes(args.action)) throw new Error('pointer action must be move/down/up');

Type guard

function isPointerAction(a) { return a === 'move' || a === 'down' || a === 'up'; }

Prevention

When it happens

Trigger: Calling resolveTool for "pointer" with action:"press", action:"drag", a missing action field, or a wrong-typed action like the number 1.

Common situations: A model writes "click" instead of "move"/"down"/"up" (clicking is the separate click tool); the action field is omitted entirely; case mismatch like "Move".

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/755b677afb3fbbf8. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/tools.mjs:778

      const clicks = args.clicks ?? 1;
      const rest = { ...args };
      delete rest.button;
      delete rest.clicks;
      const wire = button === "left" && clicks === 1 ? "left_click"
        : button === "left" && clicks === 2 ? "double_click"
        : button === "left" && clicks === 3 ? "triple_click"
        : button === "right" && clicks === 1 ? "right_click"
        : button === "middle" && clicks === 1 ? "middle_click"
        : null;
      if (!wire) throw bad(`click supports left with 1-3 clicks, right x1 or middle x1 (got ${JSON.stringify(button)} x${clicks})`);
      if (button !== "left") delete rest.strategy; // strategy is an a11y-left-click concept
      return { name: wire, args: rest };
    }
    case "pointer": {
      const rest = { ...args };
      delete rest.action;
      const wire = { move: "mouse_move", down: "left_mouse_down", up: "left_mouse_up" }[args.action];
      if (!wire) throw bad(`pointer action must be "move", "down" or "up" (got ${JSON.stringify(args.action)})`);
      return { name: wire, args: rest };
    }
    case "clipboard": {
      const rest = { ...args };
      delete rest.action;
      if (args.action === "read") return { name: "read_clipboard", args: { computer: rest.computer } };
      if (args.action === "write") {
        if (typeof rest.text !== "string") throw bad("clipboard action \"write\" requires text");
        return { name: "write_clipboard", args: { text: rest.text, computer: rest.computer } };
      }
      throw bad(`clipboard action must be "read" or "write" (got ${JSON.stringify(args.action)})`);
    }
    case "recording": {
      const rest = { ...args };
      delete rest.action;
      const wire = { start: "recording_start", stop: "recording_stop", status: "recording_status", list: "recording_list" }[args.action];
      if (!wire) throw bad(`recording action must be start, stop, status or list (got ${JSON.stringify(args.action)})`);
      if ((args.action === "stop" || args.action === "status") && rest.id == null) throw bad(`recording action "${args.action}" requires id`);

View on GitHub (pinned to 73e0f67d83)