Hmbown/CodeWhale · error · ExecError

unsupported_selector

unsupported_selector

Error message

Windows semantic actions do not support explicit application or window selectors

What it means

The Windows (win32) computer-use backend cannot route semantic actions to a specific application or window because it drives UI Automation through PowerShell with no window-targeting primitive. assertUntargetedElement rejects any target containing app_ref, windowIndex, or window_id with code 'unsupported_selector'. Actions therefore always apply to the currently focused element/desktop context.

Solutions

  1. Remove app_ref/windowIndex/window_id from the target and instead focus the window first (e.g. via a platform-appropriate activate/open step) before performing the action
  2. Branch on platform and use a backend that supports selectors for targeted actions
  3. Update the tool schema on Windows hosts so selector fields are not offered

Example fix

// before
await backend.action({ kind: 'click', window_id: 42 });
// after
await backend.action({ kind: 'activate', name: 'Notepad' }); // focus first, no selector on the action
await backend.action({ kind: 'click' });
Defensive patterns

Strategy: validation

Validate before calling

const UNSUPPORTED = ['app_ref', 'windowIndex', 'window_id'];
if (UNSUPPORTED.some((k) => Object.hasOwn(target, k)))
  throw new Error('strip selectors before Windows semantic actions');

Type guard

function isUntargeted(target) {
  return !['app_ref', 'windowIndex', 'window_id'].some((k) => Object.hasOwn(target ?? {}, k));
}

Try / catch

try {
  await backend.action({ kind, ...target });
} catch (e) {
  if (e.code === 'unsupported_selector') {
    await focusWindowFirst();
    await backend.action({ kind });
  } else throw e;
}

Prevention

When it happens

Trigger: Invoking any Windows semantic action with a target object that has an app_ref, windowIndex, or window_id property, e.g. {kind:'click', window_id:123} or {kind:'type', app_ref:{name:'Notepad'}}.

Common situations: Sharing one tool-call payload across macOS/Linux/Windows backends where the platform-generic schema allows selectors; porting an automation script written against another OS backend; an agent emitting cross-platform selectors that only some backends support.

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@433685b202 (2026-09-15). Data as JSON: /api/errors/80c200db3746e408. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/win32.mjs:104

// can never register the User32 type for later spawns. Each User32-backed
// invocation therefore carries its own type definition via this prelude.
// Compilation, conversion and native-call exceptions must stop before success.
const USER32_PRELUDE = `$ErrorActionPreference = 'Stop';\nAdd-Type -TypeDefinition @'\n${USER32}\n'@ -ErrorAction Stop;`;

/** Coordinate clicks on this backend are always raw pointer events; strategy="a11y" must fail closed rather than silently degrade. */
function assertEventStrategy(strategy) {
  if (strategy != null && strategy !== "auto" && strategy !== "event") {
    throw new ExecError(`strategy "${strategy}" is macOS-only; this backend dispatches coordinate clicks as raw pointer events — use an element target for a semantic action`);
  }
}

function unsupportedSelector(message) {
  return Object.assign(new ExecError(message), { code: "unsupported_selector" });
}

function assertUntargetedElement(target) {
  if (["app_ref", "windowIndex", "window_id"].some((key) => Object.hasOwn(target, key))) {
    throw unsupportedSelector("Windows semantic actions do not support explicit application or window selectors");
  }
}

export function create(opts = {}) {
  // Allow tests (and other embedders) to inject a runner so no real
  // powershell.exe is spawned. Production uses the imported runner.
  const injectedRun = opts.exec && typeof opts.exec.run === "function" ? opts.exec.run : null;
  const runner = injectedRun ?? run;

  function requireInputOwner() {
    if (opts.exec?.persistentInputOwner !== true) throw Object.assign(new ExecError(
      "This held-input gesture requires a connected Codewhale Computer Use desktop helper so a disconnected client cannot leave keys or buttons pressed. Start the helper and reconnect before retrying."
    ), { code: "input_owner_required" });
  }

  async function ps(script, o = {}) {
    throwIfAborted();
    const encoded = Buffer.from(script, "utf16le").toString("base64");

View on GitHub (pinned to 433685b202)