FoundationAgents/MetaGPT · error · ValueError
Element not found
Error message
Element not found
What it means
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.
Source
Thrown at metagpt/utils/a11y_tree.py:157
await page.evaluate(
"(document.scrollingElement || document.body).scrollTop = (document.scrollingElement || document.body).scrollTop + window.innerHeight;"
)
async def key_press(page: Page, key: str) -> None:
"""Press a key."""
if "Meta" in key and "Mac" not in await page.evaluate("navigator.platform"):
key = key.replace("Meta", "Control")
await page.keyboard.press(key)
async def get_element_outer_html(page: Page, backend_node_id: int):
cdp_session = await get_page_cdp_session(page)
try:
outer_html = await cdp_session.send("DOM.getOuterHTML", {"backendNodeId": int(backend_node_id)})
return outer_html["outerHTML"]
except Exception as e:
raise ValueError("Element not found") from e
async def get_element_center(node_info):
x, y, width, height = node_info["x"], node_info["y"], node_info["width"], node_info["height"]
center_x = x + width / 2
center_y = y + height / 2
return center_x, center_y
def extract_step(response: str, action_splitter: str = "```") -> str:
# find the first occurence of action
pattern = rf"{action_splitter}((.|\n)*?){action_splitter}"
match = re.search(pattern, response)
if match:
return match.group(1).strip()
else:
raise ValueError(f'Cannot find the answer phrase "{response}"')
View on GitHub (pinned to 11cdf466d0)
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.
Example fix
# before html = await get_element_outer_html(page, stale_backend_node_id) # after from metagpt.utils.a11y_tree import get_backend_node_id backend_id = get_backend_node_id(element_id, await tree()) # refresh tree first html = await get_element_outer_html(page, backend_id)
Defensive patterns
Strategy: retry
Validate before calling
def node_in_tree(backend_node_id, tree) -> bool:
return any(i.get("backendDOMNodeId") == backend_node_id for i in tree) Try / catch
try:
html = await get_element_outer_html(page, backend_id)
except ValueError as e:
if 'Element not found' in str(e):
tree = await get_accessibility_tree(page) # refresh
backend_id = get_backend_node_id(element_id, tree)
html = await get_element_outer_html(page, backend_id) Prevention
- 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.
When it happens
Trigger: 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().
Common situations: 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'.
Related errors
- Element {element_id} not found
- No file open. Use the open_file function first.
- File {path} not found
- Line number must be between 1 and {total_lines}
- File '{filename}' already exists.
AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14).
Data as JSON: /api/errors/0e0444998651a3ba.
Report an issue: GitHub.