{"record":{"id":"0e0444998651a3ba","repo":"FoundationAgents/MetaGPT","slug":"element-not-found","errorCode":null,"errorMessage":"Element not found","messagePattern":"Element not found","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"metagpt/utils/a11y_tree.py","lineNumber":157,"sourceCode":"        await page.evaluate(\n            \"(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop + window.innerHeight;\"\n        )\n\n\nasync def key_press(page: Page, key: str) -> None:\n    \"\"\"Press a key.\"\"\"\n    if \"Meta\" in key and \"Mac\" not in await page.evaluate(\"navigator.platform\"):\n        key = key.replace(\"Meta\", \"Control\")\n    await page.keyboard.press(key)\n\n\nasync def get_element_outer_html(page: Page, backend_node_id: int):\n    cdp_session = await get_page_cdp_session(page)\n    try:\n        outer_html = await cdp_session.send(\"DOM.getOuterHTML\", {\"backendNodeId\": int(backend_node_id)})\n        return outer_html[\"outerHTML\"]\n    except Exception as e:\n        raise ValueError(\"Element not found\") from e\n\n\nasync def get_element_center(node_info):\n    x, y, width, height = node_info[\"x\"], node_info[\"y\"], node_info[\"width\"], node_info[\"height\"]\n    center_x = x + width / 2\n    center_y = y + height / 2\n    return center_x, center_y\n\n\ndef extract_step(response: str, action_splitter: str = \"```\") -> str:\n    # find the first occurence of action\n    pattern = rf\"{action_splitter}((.|\\n)*?){action_splitter}\"\n    match = re.search(pattern, response)\n    if match:\n        return match.group(1).strip()\n    else:\n        raise ValueError(f'Cannot find the answer phrase \"{response}\"')\n","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/FoundationAgents/MetaGPT/blob/11cdf466d042aece04fc6cfd13b28e1a70341b1f/metagpt/utils/a11y_tree.py#L139-L175","documentation":"get_element_outer_html in metagpt/utils/a11y_tree.py sends the Chrome DevTools Protocol command DOM.getOuterHTML with a backendNodeId. If the CDP call raises (stale backendNodeId after DOM mutation/navigation, detached node, or closed page), the original exception is chained into this ValueError.","triggerScenarios":"Calling get_element_outer_html(page, backend_node_id) after the page navigated or the DOM changed since the accessibility tree was captured; passing a backendDOMNodeId of 0/None; calling after page.close().","commonSituations":"The accessibility tree snapshot is older than the live DOM — the element was removed by JS, replaced by a re-render (React/Vue), or the page navigated. The backendNodeId then no longer resolves, CDP throws, and this wrapper converts it to 'Element not found'.","solutions":["Re-capture the accessibility tree and re-resolve the element id to a fresh backendDOMNodeId before retrying.","Verify the node still exists via the current tree (get_backend_node_id) before calling.","Re-raise the underlying cause: inspect e via 'raise ... from e' output or debug logging to distinguish stale node from protocol/session errors.","If the page navigated, get a new page/cdp session and repeat the observation step."],"exampleFix":"# before\nhtml = await get_element_outer_html(page, stale_backend_node_id)\n\n# after\nfrom metagpt.utils.a11y_tree import get_backend_node_id\nbackend_id = get_backend_node_id(element_id, await tree())  # refresh tree first\nhtml = await get_element_outer_html(page, backend_id)","handlingStrategy":"retry","validationCode":"def node_in_tree(backend_node_id, tree) -> bool:\n    return any(i.get(\"backendDOMNodeId\") == backend_node_id for i in tree)","typeGuard":null,"tryCatchPattern":"try:\n    html = await get_element_outer_html(page, backend_id)\nexcept ValueError as e:\n    if 'Element not found' in str(e):\n        tree = await get_accessibility_tree(page)  # refresh\n        backend_id = get_backend_node_id(element_id, tree)\n        html = await get_element_outer_html(page, backend_id)","preventionTips":["Always re-capture the accessibility tree right before DOM operations.","Treat backendDOMNodeId as valid only for the snapshot it came from.","Inspect the chained exception (__cause__) when the generic message is ambiguous."],"tags":["metagpt","playwright","cdp","dom","stale-reference"],"backgroundTag":null,"analyzedSha":"11cdf466d042aece04fc6cfd13b28e1a70341b1f","analyzedAt":"2026-08-14T23:20:02.994Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}