{"record":{"id":"e424dd8764981474","repo":"alibaba/page-agent","slug":"element-at-index-index-is-not-an-htmlelement","errorCode":null,"errorMessage":"Element at index ${index} is not an HTMLElement","messagePattern":"Element at index (.+?) is not an HTMLElement","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/page-controller/src/actions.ts","lineNumber":38,"sourceCode":" * Get the HTMLElement by index from a selectorMap.\n * @private Internal method, subject to change at any time.\n */\nexport function getElementByIndex(\n\tselectorMap: Map<number, InteractiveElementDomNode>,\n\tindex: number\n): HTMLElement {\n\tconst interactiveNode = selectorMap.get(index)\n\tif (!interactiveNode) {\n\t\tthrow new Error(`No interactive element found at index ${index}`)\n\t}\n\n\tconst element = interactiveNode.ref\n\tif (!element) {\n\t\tthrow new Error(`Element at index ${index} does not have a reference`)\n\t}\n\n\tif (!isHTMLElement(element)) {\n\t\tthrow new Error(`Element at index ${index} is not an HTMLElement`)\n\t}\n\n\treturn element\n}\n\nlet lastClickedElement: HTMLElement | null = null\n\nfunction blurLastClickedElement() {\n\tif (lastClickedElement) {\n\t\tlastClickedElement.dispatchEvent(new PointerEvent('pointerout', { bubbles: true }))\n\t\tlastClickedElement.dispatchEvent(new PointerEvent('pointerleave', { bubbles: false }))\n\t\tlastClickedElement.dispatchEvent(new MouseEvent('mouseout', { bubbles: true }))\n\t\tlastClickedElement.dispatchEvent(new MouseEvent('mouseleave', { bubbles: false }))\n\t\tlastClickedElement.blur()\n\t\tlastClickedElement = null\n\t}\n}\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/alibaba/page-agent/blob/d02db1ee7c41f5315beda88bab2fe935c580f662/packages/page-controller/src/actions.ts#L20-L56","documentation":"The indexed node's ref resolved to something that is not an HTMLElement (e.g., an SVGElement, a text node, or a non-element object). All actions (click, input, select) require a real HTMLElement with the full element API, so the library guards the type before acting. It usually indicates an unexpected object ended up in the selector map or cross-realm elements from another frame/document.","triggerScenarios":"The indexed element is an SVG element or other non-HTML element; the ref came from a different document/iframe realm so instanceof HTMLElement fails; a mock or test double was injected into the tree.","commonSituations":"Pages with SVG-heavy UIs where an SVG node got flagged interactive; jsdom/test environments where HTMLElement globals differ between copies of the library; cross-realm elements from iframes.","solutions":["Check what sits at that index via getSimplifiedHTML() and pick a real HTML element","If testing, ensure the same HTMLElement realm/global is used (single jsdom instance, single library copy)","Re-index after navigation so refs come from the current document","Report upstream if a legitimately interactive SVG is misclassified and blocks your flow"],"exampleFix":"// before\nawait controller.clickElement(3) // ref is SVGElement\n\n// after\nawait controller.updateTree()\n// target an actual HTML element index, e.g. the parent <button>:\nawait controller.clickElement(2)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"const el = controller.element?.(i); if (el instanceof HTMLElement) { /* safe to act */ }","tryCatchPattern":"try { await controller.clickElement(i) } catch (e) { if (String(e).includes('is not an HTMLElement')) { /* pick a different index */ } else throw e }","preventionTips":["Prefer indexes of real HTML controls over SVG/graphics nodes","In tests, use a single DOM library instance/realm","Re-index after cross-frame or cross-document navigation"],"tags":["dom","type-mismatch","svg","page-controller"],"backgroundTag":"unexpected-element-type","analyzedSha":"d02db1ee7c41f5315beda88bab2fe935c580f662","analyzedAt":"2026-08-28T19:39:48.634Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}