{"record":{"id":"4948cb085680c571","repo":"alibaba/page-agent","slug":"element-at-index-index-does-not-have-a-referenc","errorCode":null,"errorMessage":"Element at index ${index} does not have a reference","messagePattern":"Element at index (.+?) does not have a reference","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/page-controller/src/actions.ts","lineNumber":34,"sourceCode":"\twaitFor,\n} from './utils'\n\n/**\n * 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()","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/alibaba/page-agent/blob/d02db1ee7c41f5315beda88bab2fe935c580f662/packages/page-controller/src/actions.ts#L16-L52","documentation":"Thrown when the interactive node exists in the selector map but its ref (live HTMLElement reference) is null or missing. The DOM tree keeps lightweight nodes with a back-reference to the real element; a missing ref means the node was serialized/detached or the element was removed from the document since indexing. The library refuses to operate on a node it cannot resolve to a real element.","triggerScenarios":"Element was removed from the DOM (SPA rerender, route change, list virtualization) between updateTree() and the action; tree data was structured-cloned or serialized across contexts (extension content script vs background), dropping the live ref; corrupted/partial indexing.","commonSituations":"Browser extension passing the dom tree through message passing which strips element references; fast-changing pages where nodes detach quickly; memory-pressure cleanup clearing refs.","solutions":["Re-index with updateTree() and immediately perform the action on the fresh tree","Do not serialize or structuredClone the selector map/nodes; keep actions in the same context where indexing happened","If the element is genuinely gone, re-observe and choose an equivalent element","Reduce the delay between observation and action on dynamic pages"],"exampleFix":"// before\nawait controller.inputText(7, 'hello') // node.ref === null\n\n// after\nawait controller.updateTree()\nawait controller.inputText(7, 'hello') // fresh refs","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await controller.inputText(i, text) } catch (e) { if (String(e).includes('does not have a reference')) { await controller.updateTree(); await controller.inputText(i, text) } else throw e }","preventionTips":["Keep indexing and actions in the same JS context; never structuredClone or message-pass the selector map","Minimize delay between updateTree() and actions on dynamic pages","Re-index after SPA route changes or list re-renders"],"tags":["dom","detached-element","stale-reference","page-controller"],"backgroundTag":"stale-dom-reference","analyzedSha":"d02db1ee7c41f5315beda88bab2fe935c580f662","analyzedAt":"2026-08-28T19:39:48.634Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}