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

  1. Re-capture the accessibility tree and re-resolve the element id to a fresh backendDOMNodeId before retrying.
  2. Verify the node still exists via the current tree (get_backend_node_id) before calling.
  3. Re-raise the underlying cause: inspect e via 'raise ... from e' output or debug logging to distinguish stale node from protocol/session errors.
  4. 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

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


AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14). Data as JSON: /api/errors/0e0444998651a3ba. Report an issue: GitHub.