fix(browser): recover from CDP DOM-node serialization crash in browser_console (#35385)

browser_console(expression="document.body") returned the cryptic CDP error
"Object reference chain is too long" instead of a usable result.

With returnByValue=true, Chrome deep-serializes the eval result; for a live
DOM Node/NodeList/Window that serialization overruns CDP's recursion guard
and fails the whole call with a protocol-level error (not a JS exception),
which _browser_eval surfaced raw.

- browser_supervisor.evaluate_runtime: on that specific error, retry once
  with returnByValue=false so Chrome returns the node's description string —
  the same graceful path already used for document.querySelector() results.
- browser_tool._browser_eval (CLI subprocess fallback): the subprocess can't
  retry, so convert the reference-chain error into actionable guidance
  (extract a primitive / use JSON.stringify) instead of leaking it raw.

No expression rewriting — normal evals (1+41 -> 42) are untouched.
This commit is contained in:
Teknium
2026-05-30 07:31:25 -07:00
committed by GitHub
parent 42bbd221e8
commit 92ad7cc62c
3 changed files with 156 additions and 8 deletions
+16
View File
@@ -2874,6 +2874,22 @@ def _browser_eval(expression: str, task_id: Optional[str] = None) -> str:
"error": f"JavaScript evaluation is not supported by this browser backend. {err}",
}
return json.dumps(_copy_fallback_warning(response, result))
# A live DOM node / NodeList / Window can't be JSON-serialized by CDP
# and fails the eval with "Object reference chain is too long". The
# supervisor fast path retries with returnByValue=false, but the CLI
# subprocess can't, so turn the cryptic protocol error into actionable
# guidance instead of surfacing it raw.
if "reference chain is too long" in err.lower():
response = {
"success": False,
"error": (
"Expression returned a live DOM node / NodeList / Window, "
"which can't be serialized. Extract a primitive value "
"(e.g. .innerText, .href, .src, .value) or use "
"JSON.stringify() / a snapshot tool instead."
),
}
return json.dumps(_copy_fallback_warning(response, result))
response = {
"success": False,
"error": err,