Hmbown/CodeWhale · error · ExecError

strategy "a11y" is only available for a left single click…

Error message

strategy "a11y" is only available for a left single click on this backend; ${mouseName(button)} x${clicks} has no accessibility equivalent

What it means

The a11y strategy is only implemented for a left single click, which maps to a semantic accessibility action (e.g. AXPress). Other button/click combinations (right-click, double-click, drag, middle button) have no accessibility equivalent on this backend, so requesting strategy='a11y' for them is rejected up front.

Solutions

  1. Use strategy='a11y' only for left single clicks
  2. For right-click or multi-click, use strategy='app' (window-scoped pointer) or 'event'
  3. Gate the strategy choice on the button/clicks values in the caller
  4. For a context menu, check whether the target exposes a dedicated AX action (e.g. ShowMenu) instead of synthesizing a right-click

Example fix

// before
await click(button, x, y, { clicks: 2, strategy: "a11y" });
// after
const strategy = button === "left" && clicks === 1 ? "a11y" : "app";
await click(button, x, y, { clicks, strategy });
Defensive patterns

Strategy: validation

Validate before calling

if (strategy === "a11y" && !(button === "left" && clicks === 1)) {
  strategy = "app"; // or throw at your boundary
}

Type guard

const a11yCapable = (button, clicks) => button === "left" && clicks === 1;

Prevention

When it happens

Trigger: pointerClick called with strategy='a11y' and any combination other than button='left' with clicks=1 — e.g. right/context click, double click, triple click.

Common situations: Caller wants a right-click context menu via accessibility; scripts parameterize button/clicks and pass strategy='a11y' unconditionally.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:436

   * strategy="event" goes straight to the guarded global gesture.
   */
  async function pointerClick(button, x, y, clicks, strategy = "auto") {
    assertInScreen(x, y);
    if (!["auto", "a11y", "event", "app"].includes(strategy)) throw new ExecError(`strategy must be auto, a11y, app or event (got ${JSON.stringify(strategy)})`);
    let a11yReason = null;
    if (strategy !== "event" && ["left", "right"].includes(button) && clicks === 1) {
      if (button === "right") await requireBackgroundActions();
      const hit = await native("hit_test", { x, y, perform: true, ...(button === "right" ? { operation: "context" } : {}) });
      if (hit?.action_sent) {
        return { action_sent: true, strategy: "a11y", action: hit.action, pointer_moved: false, at: { x, y }, button, clicks,
                 element: { role: hit.element?.role ?? null, label: hit.element?.label ?? null } };
      }
      a11yReason = hit?.reason ?? "not_found";
      if (strategy === "a11y") {
        throw new ExecError(`no supported accessibility click at (${x}, ${y}) in the bound application (${a11yReason}) — observe the available actions, use strategy "app" for a window-scoped pointer click, or a separate computer`);
      }
    } else if (strategy === "a11y") {
      throw new ExecError(`strategy "a11y" is only available for a left single click on this backend; ${mouseName(button)} x${clicks} has no accessibility equivalent`);
    }
    if (strategy === "app" || (strategy === "auto" && !state.foregroundInput)) {
      // Window-routed record delivery: AppKit accepts the events as genuine
      // input, the cursor never moves. A momentary no-raise front lease is
      // taken and restored inside the helper; it is reported, not hidden.
      if ((await native("input_capabilities"))?.window_record === 1) {
        // Ownership is enforced by window containment inside the helper: the
        // events are addressed to a window id of the bound app, so a covered
        // background window is still safe — they cannot land on the coverer.
        const r = await native("bg_pointer", { steps: clickSteps(button, x, y, clicks),
          ...(a11yReason === "web_popup_requires_real_click" ? { menu_poll_ms: 6000 } : {}) });
        return { action_sent: true, strategy: "window-record", input_scope: "application-window",
                 at: { x, y }, button, clicks, pointer_moved: false, front_lease: r.front_lease ?? true,
                 ...leaseAccounting(r),
                 ...(r.menu_lease_held ? { menu_lease_held: true } : {}),
                 window: r.window ?? null,
                 ...(a11yReason ? { a11y_reason: a11yReason } : {}) };
      }

View on GitHub (pinned to 73e0f67d83)