{"record":{"id":"9f030181711ee4dc","repo":"Hmbown/CodeWhale","slug":"element-no-geometry","errorCode":"element_no_geometry","errorMessage":"element ${target.index} of ${where} has no cached geometry — use a coordinate target","messagePattern":"element (.+?) of (.+?) has no cached geometry — use a coordinate target","errorType":"error_code","errorClass":"ServerError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/mcp/server.mjs","lineNumber":240,"sourceCode":"      // In-place replacement: same role and geometry but a different label is\n      // still a different element (e.g. \"Load\" → \"Confirm\").\n      if (fresh.label !== element.label) {\n        throw new ServerError(\"element_stale\", `element ${target.index} of ${where} changed label (${element.label} → ${fresh.label}) — call get_app_state again`);\n      }\n    }\n    if (kind === \"semantic\") {\n      return {\n        app_ref: state.app_ref, windowIndex: element.windowIndex ?? 0, path: element.path,\n        strategy: \"a11y\", role: element.role, label: element.label, reacquired: false,\n        ...(element.runtime_id ? { runtime_id: element.runtime_id, window_runtime_id: element.window_runtime_id } : {}),\n      };\n    }\n    const moved = !!fresh && (\n      fresh.position?.x !== element.position?.x || fresh.position?.y !== element.position?.y ||\n      fresh.size?.w !== element.size?.w || fresh.size?.h !== element.size?.h);\n    const pos = fresh?.position ?? element.position;\n    const sz = fresh?.size ?? element.size;\n    if (!pos || !sz) throw new ServerError(\"element_no_geometry\", `element ${target.index} of ${where} has no cached geometry — use a coordinate target`);\n    if (moved && sink) sink.reacquired = true;\n    // Keep the element identity as well as geometry: semantic clicks must not\n    // substitute whichever element happens to occupy an oversized AX center.\n    const c = { x: Math.round(pos.x + sz.w / 2), y: Math.round(pos.y + sz.h / 2) };\n    return { ...c, strategy: \"a11y-center\", role: element.role, label: element.label, app_ref: state.app_ref,\n      windowIndex: element.windowIndex ?? 0, path: element.path, reacquired: moved };\n  }\n  throw new ServerError(\"bad_target\", \"target must be {type:'coordinate',x,y} or {type:'element',index} (state_id optional to pin a specific observation)\");\n}\n\nfunction bindRaster(computer, shot) {\n  lastRasters.set(computer.id, {\n    file: shot.file ?? shot.path,\n    scale: shot.scale ?? 1,\n    origin: shot.points ?? { x: 0, y: 0 },\n    pixels: shot.pixels ?? null,\n    capturedAt: shot.capturedAt ?? new Date().toISOString(),\n  });","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/mcp/server.mjs#L222-L258","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a coordinate target {type:'coordinate',x,y} derived from the screenshot instead of the element target.","Call get_app_state with detail:'full' to obtain elements that include position and size.","Re-observe to refresh the raster binding, then retry the element target.","Pick a different element in the tree that carries bounds."],"exampleFix":"// before\nawait click({ target: { type: 'element', index: 5 } });\n// after\nif (el.position && el.size) {\n  await click({ target: { type: 'element', index: el.index } });\n} else {\n  await click({ target: { type: 'coordinate', x: el.centerX, y: el.centerY } });\n}","handlingStrategy":"fallback","validationCode":"function hasGeometry(el) { return !!(el && el.position && el.size && typeof el.position.x === 'number' && typeof el.size.w === 'number'); }","typeGuard":"function hasGeometry(el) { return !!el?.position && !!el?.size; }","tryCatchPattern":"try { await click({ target: { type: 'element', index } }); }\ncatch (e) { if (e?.code === 'element_no_geometry') { await click({ target: { type: 'coordinate', x, y } }); } else throw e; }","preventionTips":["Request detail:'full' observations when you need element bounds.","Prefer coordinate targets for elements known to lack bounds.","Re-observe before acting so fresh geometry is available.","Filter out boundless container nodes when choosing targets."],"tags":["geometry","accessibility","missing-data"],"backgroundTag":"missing-required-argument","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}