Skip to content

Conversation

@shrey150
Copy link
Contributor

@shrey150 shrey150 commented Jan 5, 2026

Summary

  • Add handling for page close events to automatically update the active page when the current page closes
  • Add error handling for CDP operations to gracefully handle closed targets
  • Clean up unnecessary inline comments

Test plan

  • Verify multi-tab scenarios work correctly
  • Confirm page close events properly switch active page
  • Test that closed target errors are handled gracefully

🤖 Generated with Claude Code


Summary by cubic

Keeps the active page in sync when tabs close and prevents CDP errors on closed targets. Improves multi-tab stability and avoids crashes when a page or context shuts down.

  • Bug Fixes

    • Switch active page to another open tab when the current one closes.
    • Guard CDP session creation and Page.enable to gracefully handle closed targets.
    • Skip listeners and operations if a page is already closed.
  • Refactors

    • Remove unnecessary inline comments in StagehandContext.

Written for commit 0e2e5ac. Summary will update on new commits.

@changeset-bot
Copy link

changeset-bot bot commented Jan 5, 2026

⚠️ No Changeset found

Latest commit: 0e2e5ac

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

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

- Add handling for page close events to update active page when current page closes
- Add error handling for CDP operations on closed targets
- Remove unnecessary inline comments

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@shrey150 shrey150 closed this Jan 5, 2026
@shrey150 shrey150 deleted the support/mahesh branch January 5, 2026 20:01
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 5, 2026

Greptile Summary

This PR adds automatic page switching when pages close and graceful error handling for CDP operations on closed targets. The changes help prevent errors in multi-tab scenarios by detecting closed pages and switching focus to remaining open pages.

Key changes:

  • Added isClosed() checks before operating on pages
  • Added error handling for "Target closed" errors in CDP operations
  • Registered close event handlers to automatically switch active page when current page closes
  • Removed unnecessary inline comments

Issues found:

  • Duplicate close event handlers registered (3 total per page) causing redundant active page switching logic
  • Potential race condition where pages could close between isClosed() check and handler registration
  • context.pages() may return closing pages that should be filtered out before setting as active page

Confidence Score: 2/5

  • This PR introduces important error handling but has logic issues with duplicate event handlers and race conditions that could cause unexpected behavior
  • The PR correctly identifies and attempts to solve real problems (handling closed pages and CDP errors), but the implementation has multiple logical issues: (1) three duplicate close handlers per page causing redundant operations, (2) race conditions where pages could close between checks and handler registration, and (3) failure to filter closed pages from context.pages() before setting active page. While these issues may not cause critical failures, they create unnecessary complexity and potential edge case bugs in multi-tab scenarios.
  • lib/StagehandContext.ts needs attention - specifically the duplicate close handlers in handleNewPlaywrightPage and attachFrameNavigatedListener, plus the filtering of closed pages when selecting a new active page

Important Files Changed

Filename Overview
lib/StagehandContext.ts Added page close event handling and CDP error handling, but has duplicate close handlers and potential race conditions with page closure timing

Sequence Diagram

sequenceDiagram
    participant Context as BrowserContext
    participant SC as StagehandContext
    participant Page as PlaywrightPage
    participant Handler as Event Handlers
    
    Note over Context,Handler: New Page Created
    Context->>SC: emit "page" event
    SC->>SC: handleNewPlaywrightPage (1st call)
    SC->>Handler: Register close handler #1
    SC->>Page: Check if closed
    alt Page already closed
        SC-->>Context: Return early
    else Page open
        SC->>SC: Create/get StagehandPage
        SC->>SC: setActivePage
    end
    
    SC->>SC: attachFrameNavigatedListener
    SC->>Handler: Register close handler #2
    SC->>Page: Create CDP session
    
    SC->>SC: handleNewPlaywrightPage (2nd call in finally)
    SC->>Handler: Register close handler #3
    
    Note over Context,Handler: Page Closes
    Page->>Handler: emit "close" event
    Handler->>SC: close handler #1 fires
    SC->>SC: Get open pages, setActivePage
    Handler->>SC: close handler #2 fires
    SC->>SC: Unregister frameId
    SC->>SC: Get open pages, setActivePage (duplicate)
    Handler->>SC: close handler #3 fires
    SC->>SC: Get open pages, setActivePage (duplicate)
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.

Additional Comments (2)

  1. lib/StagehandContext.ts, line 176-188 (link)

    logic: duplicate close handler registered - handleNewPlaywrightPage is called twice per page (lines 96 and 107), registering this close handler twice. combined with the close handler in attachFrameNavigatedListener (lines 233-248), this creates 3 handlers that all attempt to switch the active page when a page closes. while functionally this may work due to the conditional check, it causes unnecessary iterations and setActivePage calls.

  2. lib/StagehandContext.ts, line 174 (link)

    logic: race condition - checking isClosed() before registering the close handler doesn't prevent the page from closing between the check and the handler registration. if the page closes after line 174 but before line 176, the close handler won't fire and the active page won't be updated.

    the current pattern should use a try-catch around the once registration or check isClosed() again inside the handler. is this scenario (page closing between isClosed check and handler registration) a realistic concern in your testing, or is the check primarily for performance optimization?

1 file reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +233 to +248
msg.includes("Target closed") ||
msg.includes("Target page, context or browser has been closed")
) {
return;
}
throw err;
}

pwPage.once("close", () => {
if (shPage.frameId) this.unregisterFrameId(shPage.frameId);

if (this.activeStagehandPage === shPage) {
const openPages = this.intContext.pages();
if (openPages.length > 0) {
for (const p of openPages) {
const sp = this.pageMap.get(p);
Copy link
Contributor

Choose a reason for hiding this comment

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

style: duplicate close event logic - this close handler has the same active page switching logic as the one in handleNewPlaywrightPage (lines 176-188). the only difference is line 234 which unregisters the frame ID. consider consolidating into a single close handler or removing the duplicate switching logic from one of them.

Suggested change
msg.includes("Target closed") ||
msg.includes("Target page, context or browser has been closed")
) {
return;
}
throw err;
}
pwPage.once("close", () => {
if (shPage.frameId) this.unregisterFrameId(shPage.frameId);
if (this.activeStagehandPage === shPage) {
const openPages = this.intContext.pages();
if (openPages.length > 0) {
for (const p of openPages) {
const sp = this.pageMap.get(p);
pwPage.once("close", () => {
if (shPage.frameId) this.unregisterFrameId(shPage.frameId);
// Active page switching is handled in handleNewPlaywrightPage
});

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/StagehandContext.ts
Line: 233:248

Comment:
**style:** duplicate close event logic - this close handler has the same active page switching logic as the one in `handleNewPlaywrightPage` (lines 176-188). the only difference is line 234 which unregisters the frame ID. consider consolidating into a single close handler or removing the duplicate switching logic from one of them.

```suggestion
    pwPage.once("close", () => {
      if (shPage.frameId) this.unregisterFrameId(shPage.frameId);
      // Active page switching is handled in handleNewPlaywrightPage
    });
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.


public getActivePage(): StagehandPage | null {
return this.activeStagehandPage;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: this.intContext.pages() may include the closing page - when the close event fires, context.pages() might still return the page that's being closed. the loop should verify each page isn't closed before setting it as active.

Suggested change
}
const openPages = this.intContext.pages().filter(p => !p.isClosed());
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/StagehandContext.ts
Line: 179:179

Comment:
**logic:** `this.intContext.pages()` may include the closing page - when the close event fires, `context.pages()` might still return the page that's being closed. the loop should verify each page isn't closed before setting it as active.

```suggestion
        const openPages = this.intContext.pages().filter(p => !p.isClosed());
```

How can I resolve this? If you propose a fix, please make it concise.

) {
return;
}
throw err;
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: same issue - filter out closed pages before attempting to set as active

Suggested change
throw err;
const openPages = this.intContext.pages().filter(p => !p.isClosed());
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/StagehandContext.ts
Line: 238:238

Comment:
**logic:** same issue - filter out closed pages before attempting to set as active

```suggestion
        const openPages = this.intContext.pages().filter(p => !p.isClosed());
```

How can I resolve this? If you propose a fix, please make it concise.

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.

1 issue found across 1 file

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="lib/StagehandContext.ts">

<violation number="1" location="lib/StagehandContext.ts:181">
P1: Same type mismatch issue: `this.intContext.pages()` returns wrapped `Page[]` objects, but `pageMap` expects `PlaywrightPage` keys. This lookup will always fail, preventing the active page switching from working.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

if (shPage && this.activeStagehandPage === shPage) {
const openPages = this.intContext.pages();
for (const p of openPages) {
const sp = this.pageMap.get(p);
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 5, 2026

Choose a reason for hiding this comment

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

P1: Same type mismatch issue: this.intContext.pages() returns wrapped Page[] objects, but pageMap expects PlaywrightPage keys. This lookup will always fail, preventing the active page switching from working.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/StagehandContext.ts, line 181:

<comment>Same type mismatch issue: `this.intContext.pages()` returns wrapped `Page[]` objects, but `pageMap` expects `PlaywrightPage` keys. This lookup will always fail, preventing the active page switching from working.</comment>

<file context>
@@ -179,23 +171,80 @@ export class StagehandContext {
+      if (shPage &amp;&amp; this.activeStagehandPage === shPage) {
+        const openPages = this.intContext.pages();
+        for (const p of openPages) {
+          const sp = this.pageMap.get(p);
+          if (sp &amp;&amp; sp !== shPage) {
+            this.setActivePage(sp);
</file context>
Fix with Cubic

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.

2 participants