Hmbown/CodeWhale · error · ServerError

element_no_geometry

element_no_geometry

Error message

element ${target.index} of ${where} has no cached geometry — use a coordinate target

What it means

normalizeTarget computes a click point from an element's cached position and size for a11y-center targeting. If neither the fresh nor cached element carries geometry (position/size), the library cannot compute a center and throws element_no_geometry, telling the caller to fall back to an explicit coordinate target.

Solutions

  1. Use a coordinate target {type:'coordinate',x,y} derived from the screenshot instead of the element target.
  2. Call get_app_state with detail:'full' to obtain elements that include position and size.
  3. Re-observe to refresh the raster binding, then retry the element target.
  4. Pick a different element in the tree that carries bounds.

Example fix

// before
await click({ target: { type: 'element', index: 5 } });
// after
if (el.position && el.size) {
  await click({ target: { type: 'element', index: el.index } });
} else {
  await click({ target: { type: 'coordinate', x: el.centerX, y: el.centerY } });
}
Defensive patterns

Strategy: fallback

Validate before calling

function hasGeometry(el) { return !!(el && el.position && el.size && typeof el.position.x === 'number' && typeof el.size.w === 'number'); }

Type guard

function hasGeometry(el) { return !!el?.position && !!el?.size; }

Try / catch

try { await click({ target: { type: 'element', index } }); }
catch (e) { if (e?.code === 'element_no_geometry') { await click({ target: { type: 'coordinate', x, y } }); } else throw e; }

Prevention

When it happens

Trigger: Using an element target for a click-type action where resolveElement returned an element whose cached position/size are missing and the live re-resolution also returned no geometry.

Common situations: Observations taken with detail:'compact' or filtered results that omit geometry; container or text-only AX nodes with no bounds; raster binding expired so fresh geometry is unavailable.

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/9f030181711ee4dc. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/mcp/server.mjs:240

      // In-place replacement: same role and geometry but a different label is
      // still a different element (e.g. "Load" → "Confirm").
      if (fresh.label !== element.label) {
        throw new ServerError("element_stale", `element ${target.index} of ${where} changed label (${element.label} → ${fresh.label}) — call get_app_state again`);
      }
    }
    if (kind === "semantic") {
      return {
        app_ref: state.app_ref, windowIndex: element.windowIndex ?? 0, path: element.path,
        strategy: "a11y", role: element.role, label: element.label, reacquired: false,
        ...(element.runtime_id ? { runtime_id: element.runtime_id, window_runtime_id: element.window_runtime_id } : {}),
      };
    }
    const moved = !!fresh && (
      fresh.position?.x !== element.position?.x || fresh.position?.y !== element.position?.y ||
      fresh.size?.w !== element.size?.w || fresh.size?.h !== element.size?.h);
    const pos = fresh?.position ?? element.position;
    const sz = fresh?.size ?? element.size;
    if (!pos || !sz) throw new ServerError("element_no_geometry", `element ${target.index} of ${where} has no cached geometry — use a coordinate target`);
    if (moved && sink) sink.reacquired = true;
    // Keep the element identity as well as geometry: semantic clicks must not
    // substitute whichever element happens to occupy an oversized AX center.
    const c = { x: Math.round(pos.x + sz.w / 2), y: Math.round(pos.y + sz.h / 2) };
    return { ...c, strategy: "a11y-center", role: element.role, label: element.label, app_ref: state.app_ref,
      windowIndex: element.windowIndex ?? 0, path: element.path, reacquired: moved };
  }
  throw new ServerError("bad_target", "target must be {type:'coordinate',x,y} or {type:'element',index} (state_id optional to pin a specific observation)");
}

function bindRaster(computer, shot) {
  lastRasters.set(computer.id, {
    file: shot.file ?? shot.path,
    scale: shot.scale ?? 1,
    origin: shot.points ?? { x: 0, y: 0 },
    pixels: shot.pixels ?? null,
    capturedAt: shot.capturedAt ?? new Date().toISOString(),
  });

View on GitHub (pinned to 73e0f67d83)