Skip to content

Conversation

@seanmcguire12
Copy link
Member

@seanmcguire12 seanmcguire12 commented Dec 16, 2025

why

  • when running on a site with deep nesting of elements/nodes, the captureHybridSnapshot() will fail and log this error:
    Error: -32000 Failed to convert response to JSON: CBOR: stack limit exceeded at position 60578
  • this is because Dom.getDocument fails

what changed

  • added handling and retries to Dom.getDocument where we exponentially reduce the depth parameter on each retry

test plan

  • added an e2e test nested-div.spec.ts just to confirm the captureHybridSnapshot() fn does not throw on the test website
  • added unit tests in snapshot-cbor.test.ts which use mock data to confirm that (1) retries are actually happening when depth is exceeded, (2) after a failed step, missing nodes are glued back into the tree, and (3) the StagehandDomProcessError is thrown when all depth attempts have failed

Summary by cubic

Prevent CBOR “stack limit exceeded” errors in captureHybridSnapshot and DOM map building on deeply nested pages. Adapt DOM depth and hydrate missing nodes so snapshots complete reliably.

  • Bug Fixes
    • Added adaptive retries for DOM.getDocument with reducing depth.
    • Hydrated truncated trees via DOM.describeNode with fallback depths; merged children/shadow roots/contentDocument.
    • Threw StagehandDomProcessError with a clear message when all attempts fail.
    • Tests: e2e to confirm no throw on a deeply nested site; unit tests for retries, hydration, and failure path.

Written for commit b11476a. Summary will update automatically on new commits.

@changeset-bot
Copy link

changeset-bot bot commented Dec 16, 2025

🦋 Changeset detected

Latest commit: b11476a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-evals Patch
@browserbasehq/stagehand-server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 16, 2025

Greptile Overview

Greptile Summary

This PR fixes a critical CBOR stack limit error that occurs when Stagehand's captureHybridSnapshot() function encounters deeply nested DOM structures. The issue manifests as -32000 Failed to convert response to JSON: CBOR: stack limit exceeded errors when Dom.getDocument tries to serialize very deep element trees.

The fix implements an adaptive retry mechanism that progressively reduces the depth parameter from -1 down to 1 when CBOR errors occur. When DOM traversal is limited by depth, a hydration system uses Dom.describeNode to patch missing children, shadow roots, and content documents back into the truncated tree. This ensures reliable snapshot collection across diverse web content while gracefully handling edge cases.

The implementation integrates with Stagehand's existing DOM processing pipeline by replacing direct Dom.getDocument calls with the new getDomTreeWithFallback function in both buildSessionDomIndex and domMapsForSession.

Important Files Changed

Filename Score Overview
packages/core/lib/v3/understudy/a11y/snapshot.ts 4/5 Core fix implementing adaptive retry logic and DOM hydration system for CBOR stack limit errors
packages/core/tests/snapshot-cbor.test.ts 5/5 Comprehensive unit tests validating retry behavior, error handling, and node hydration
packages/core/lib/v3/tests/nested-div.spec.ts 4/5 E2E test ensuring captureHybridSnapshot works on deeply nested test pages

Confidence score: 4/5

  • This PR addresses a well-defined edge case with a targeted solution that maintains backward compatibility
  • Score reflects solid implementation with comprehensive test coverage, but complexity of DOM tree manipulation introduces some risk
  • Pay close attention to the snapshot.ts file's retry logic and hydration mechanisms for potential edge cases

Sequence Diagram

sequenceDiagram
    participant User
    participant StagehandAPI as "Stagehand API"
    participant CDPSession as "CDP Session"
    participant Browser as "Browser DOM"

    User->>StagehandAPI: "Call captureHybridSnapshot(page)"
    StagehandAPI->>CDPSession: "Enable DOM, Runtime, Accessibility"
    CDPSession->>Browser: "Enable protocols"
    
    StagehandAPI->>StagehandAPI: "Call getDomTreeWithFallback(session, pierce=true)"
    
    loop "DOM Depth Attempts [-1, 256, 128, 64, 32, 16, 8, 4, 2, 1]"
        StagehandAPI->>CDPSession: "DOM.getDocument({depth: currentDepth})"
        CDPSession->>Browser: "Request DOM tree with depth limit"
        Browser-->>CDPSession: "Error: CBOR stack limit exceeded"
        CDPSession-->>StagehandAPI: "CBOR stack limit error"
        StagehandAPI->>StagehandAPI: "Check if CBOR error, continue to next depth"
    end
    
    Note over StagehandAPI: "Eventually succeeds with reduced depth (e.g., 256)"
    CDPSession->>Browser: "DOM.getDocument({depth: 256})"
    Browser-->>CDPSession: "Truncated DOM tree"
    CDPSession-->>StagehandAPI: "DOM tree with missing children"
    
    StagehandAPI->>StagehandAPI: "Call hydrateDomTree(session, root, pierce)"
    
    loop "For each node needing expansion"
        StagehandAPI->>StagehandAPI: "Check shouldExpandNode(node)"
        alt "Node has more children than returned"
            loop "Describe Depth Attempts [-1, 64, 32, 16, 8, 4, 2, 1]"
                StagehandAPI->>CDPSession: "DOM.describeNode({nodeId/backendNodeId, depth})"
                CDPSession->>Browser: "Get expanded node details"
                alt "CBOR error again"
                    Browser-->>CDPSession: "Error: CBOR stack limit exceeded"
                    CDPSession-->>StagehandAPI: "CBOR stack limit error"
                    StagehandAPI->>StagehandAPI: "Try next smaller depth"
                else "Success"
                    Browser-->>CDPSession: "Expanded node with children"
                    CDPSession-->>StagehandAPI: "Complete node details"
                    StagehandAPI->>StagehandAPI: "mergeDomNodes(target, source)"
                end
            end
        end
    end
    
    StagehandAPI->>CDPSession: "Accessibility.getFullAXTree()"
    CDPSession->>Browser: "Get accessibility tree"
    Browser-->>CDPSession: "Accessibility nodes"
    CDPSession-->>StagehandAPI: "A11y tree data"
    
    StagehandAPI->>StagehandAPI: "Build combined tree with XPath mapping"
    StagehandAPI-->>User: "Return HybridSnapshot with combinedTree, XPath maps"
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Member

@pirate pirate left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tenatively approve this PR, but it could use some more docstrings/comments explaining the logic flow / why these steps are necessary (in the code next to the logic, not just as PR comments).

also worth explaining how it interacts with iframes on different origins across a page, shadow doms, etc.

@seanmcguire12 seanmcguire12 merged commit a890f16 into main Dec 17, 2025
15 checks passed
miguelg719 pushed a commit that referenced this pull request Dec 27, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @browserbasehq/[email protected]

### Patch Changes

- [#1461](#1461)
[`0f3991e`](0f3991e)
Thanks [@tkattkat](https://github.com/tkattkat)! - Move hybrid mode out
of experimental

- [#1433](#1433)
[`e0e22e0`](e0e22e0)
Thanks [@tkattkat](https://github.com/tkattkat)! - Put hybrid mode
behind experimental

- [#1456](#1456)
[`f261051`](f261051)
Thanks [@shrey150](https://github.com/shrey150)! - Invoke page.hover for
agent move action

- [#1473](#1473)
[`e021674`](e021674)
Thanks [@shrey150](https://github.com/shrey150)! - Add safety
confirmation support for OpenAI + Google CUA

- [#1399](#1399)
[`6a5496f`](6a5496f)
Thanks [@tkattkat](https://github.com/tkattkat)! - Ensure cua agent is
killed when stagehand.close is called

- [#1436](#1436)
[`fea1700`](fea1700)
Thanks [@miguelg719](https://github.com/miguelg719)! - Fix auto-load key
for act/extract/observe parametrized models on api

- [#1439](#1439)
[`5b288d9`](5b288d9)
Thanks [@tkattkat](https://github.com/tkattkat)! - Remove base64 from
agent actions array ( still present in messages object )

- [#1408](#1408)
[`e822f5a`](e822f5a)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - allow for
act() cache hit when variable values change

- [#1472](#1472)
[`638efc7`](638efc7)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix: agent
cache not refreshed on action failure

- [#1424](#1424)
[`a890f16`](a890f16)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix:
"Error: -32000 Failed to convert response to JSON: CBOR: stack limit
exceeded"

- [#1418](#1418)
[`934f492`](934f492)
Thanks [@miguelg719](https://github.com/miguelg719)! - Cleanup handlers
and bus listeners on close

- [#1430](#1430)
[`bd2db92`](bd2db92)
Thanks [@shrey150](https://github.com/shrey150)! - Fix CUA model
coordinate translation

- [#1465](#1465)
[`51e0170`](51e0170)
Thanks [@miguelg719](https://github.com/miguelg719)! - Add media
resolution high provider option to gemini 3 hybrid agent

- [#1431](#1431)
[`05f5580`](05f5580)
Thanks [@tkattkat](https://github.com/tkattkat)! - Update the cache
handling for agent

- [#1432](#1432)
[`f56a9c2`](f56a9c2)
Thanks [@tkattkat](https://github.com/tkattkat)! - Deprecate cua: true
in favor of mode: "cua"

- [#1406](#1406)
[`b40ae11`](b40ae11)
Thanks [@tkattkat](https://github.com/tkattkat)! - Add support for
hovering with coordinates ( page.hover )

- [#1407](#1407)
[`0d2b398`](0d2b398)
Thanks [@tkattkat](https://github.com/tkattkat)! - Clean up page methods

- [#1412](#1412)
[`cd01f29`](cd01f29)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - fix: load
GOOGLE_API_KEY from .env

- [#1462](#1462)
[`a734fca`](a734fca)
Thanks [@shrey150](https://github.com/shrey150)! - fix: correctly pass
userDataDir to chrome launcher

- [#1466](#1466)
[`b342acf`](b342acf)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - move
playwright to optional dependencies

- [#1440](#1440)
[`2987cd1`](2987cd1)
Thanks [@tkattkat](https://github.com/tkattkat)! - [Feature] support
excluding tools from agent

- [#1455](#1455)
[`dfab1d5`](dfab1d5)
Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - update
aisdk client to better enforce structured output with deepseek models

- [#1428](#1428)
[`4d71162`](4d71162)
Thanks [@tkattkat](https://github.com/tkattkat)! - Add "hybrid" mode to
stagehand agent

## @browserbasehq/[email protected]

### Minor Changes

- [#1459](#1459)
[`abb3469`](abb3469)
Thanks [@monadoid](https://github.com/monadoid)! - Added building of
binaries

- [#1457](#1457)
[`5fc1281`](5fc1281)
Thanks [@monadoid](https://github.com/monadoid)! - First changeset for
stagehand-server

- [#1469](#1469)
[`d634d45`](d634d45)
Thanks [@monadoid](https://github.com/monadoid)! - Bump to test binary
builds

### Patch Changes

- Updated dependencies
\[[`0f3991e`](0f3991e),
[`e0e22e0`](e0e22e0),
[`f261051`](f261051),
[`e021674`](e021674),
[`6a5496f`](6a5496f),
[`fea1700`](fea1700),
[`5b288d9`](5b288d9),
[`e822f5a`](e822f5a),
[`638efc7`](638efc7),
[`a890f16`](a890f16),
[`934f492`](934f492),
[`bd2db92`](bd2db92),
[`51e0170`](51e0170),
[`05f5580`](05f5580),
[`f56a9c2`](f56a9c2),
[`b40ae11`](b40ae11),
[`0d2b398`](0d2b398),
[`cd01f29`](cd01f29),
[`a734fca`](a734fca),
[`b342acf`](b342acf),
[`2987cd1`](2987cd1),
[`dfab1d5`](dfab1d5),
[`4d71162`](4d71162)]:
    -   @browserbasehq/[email protected]

## @browserbasehq/[email protected]

### Patch Changes

- [#1373](#1373)
[`cadd192`](cadd192)
Thanks [@tkattkat](https://github.com/tkattkat)! - Update screenshot
collector in agent evals cli

- Updated dependencies
\[[`0f3991e`](0f3991e),
[`e0e22e0`](e0e22e0),
[`f261051`](f261051),
[`e021674`](e021674),
[`6a5496f`](6a5496f),
[`fea1700`](fea1700),
[`5b288d9`](5b288d9),
[`e822f5a`](e822f5a),
[`638efc7`](638efc7),
[`a890f16`](a890f16),
[`934f492`](934f492),
[`bd2db92`](bd2db92),
[`51e0170`](51e0170),
[`05f5580`](05f5580),
[`f56a9c2`](f56a9c2),
[`b40ae11`](b40ae11),
[`0d2b398`](0d2b398),
[`cd01f29`](cd01f29),
[`a734fca`](a734fca),
[`b342acf`](b342acf),
[`2987cd1`](2987cd1),
[`dfab1d5`](dfab1d5),
[`4d71162`](4d71162)]:
    -   @browserbasehq/[email protected]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants