Hmbown/CodeWhale · error · ExecError
strategy must be auto, a11y, app or event
Error message
strategy must be auto, a11y, app or event (got ${JSON.stringify(strategy)}) What it means
pointerClick validates the strategy argument against the closed set {auto, a11y, app, event} before doing any work. Any other string (or non-string serialized to JSON) is rejected immediately so an unknown strategy never silently degrades to a default path.
Solutions
- Use one of the exact values: auto, a11y, app or event (lowercase)
- Default to 'auto' and let the backend choose a11y vs window-record delivery
- Validate/normalize the strategy value at the call site before invoking pointerClick
- If you need a strategy that does not exist, express it via existing primitives (a11y hit_test for semantic actions, event for raw gestures)
Example fix
// before
await click(x, y, { strategy: "semantic" });
// after
await click(x, y, { strategy: "a11y" }); Defensive patterns
Strategy: validation
Validate before calling
const STRATEGIES = ["auto", "a11y", "app", "event"];
if (!STRATEGIES.includes(strategy)) throw new Error(`strategy must be one of ${STRATEGIES.join(", ")}`); Type guard
const isStrategy = (s) => typeof s === "string" && ["auto", "a11y", "app", "event"].includes(s);
Prevention
- Default to 'auto' unless you specifically need to force a path
- Keep the allowed values in a shared constant with your tool schema/enum
- Validate LLM-generated arguments against the enum before calling
When it happens
Trigger: Passing a strategy value other than the four allowed ones, e.g. 'click', 'press', 'semantic', 'Auto' (case-sensitive), a truncated/typo'd value, or JSON-serialized input where the field was misnamed.
Common situations: LLM/model-generated tool arguments inventing a strategy name; a caller upgrading from an older backend that accepted different keywords; config-driven clicks where the strategy came from user configuration.
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
- Choose approve or decline
- install source must not be empty
- invalid_container
- 1
- A pinned task provider requires an explicit model
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/92559b527a961c54.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:422
const b = buttonCode(button);
const steps = [{ type: MOUSE_MOVED, x, y, button: b, clickState: 0 }];
for (let i = 1; i <= clicks; i++) {
steps.push({ type: m.down, x, y, button: b, clickState: i });
steps.push({ type: m.up, x, y, button: b, clickState: i });
}
return steps;
}
/**
* Coordinate pointer click. A left single click is first hit-tested against
* the bound application's accessibility tree: when the point names a
* pressable element we perform its semantic action, which needs no pointer
* and no foreground. strategy="a11y" requires that and fails closed;
* 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 isView on GitHub (pinned to 73e0f67d83)