Skip to content

Handle Puppeteer browser disconnects and relaunch - #1137

Merged
philiplehmann merged 6 commits into
mainfrom
puppeteer-reopen
Aug 10, 2026
Merged

Handle Puppeteer browser disconnects and relaunch#1137
philiplehmann merged 6 commits into
mainfrom
puppeteer-reopen

Conversation

@philiplehmann

@philiplehmann philiplehmann commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Bug Fixes
    • Improved PDF rendering recovery when the browser disconnects unexpectedly.
    • Automatically closes stale browser sessions and relaunches them when needed.
    • Added handling for browsers that are already disconnected immediately after launch.
    • Prevented unnecessary browser relaunches while the renderer is shutting down.
    • Improved reliability when closing browser sessions, including cases where cleanup encounters an error.

Add cleanup logic to safely close and reset the browser instance,
then automatically relaunch when Puppeteer emits `disconnected` or
when the current browser is no longer connected.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@philiplehmann, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8a87bd5e-9f75-4237-850d-b478d41d817c

📥 Commits

Reviewing files that changed from the base of the PR and between d452981 and 3d18197.

📒 Files selected for processing (2)
  • libs/binary/puppeteer/src/lib/render-to.spec.ts
  • libs/binary/puppeteer/src/lib/render-to.ts
📝 Walkthrough

Walkthrough

BrowserToPdfRenderer now recovers disconnected browser instances through a shared promise. It cleans up stale browsers, prevents recovery during shutdown, and handles disconnect events and already-disconnected browsers.

Changes

Browser recovery

Layer / File(s) Summary
Disconnected browser cleanup and relaunch
libs/binary/puppeteer/src/lib/render-to.ts
The renderer registers one disconnect handler per browser. It cleans up disconnected browsers, shares concurrent recovery, and relaunches the browser.
Shutdown coordination and validation
libs/binary/puppeteer/src/lib/render-to.ts, libs/binary/puppeteer/src/lib/render-to.spec.ts
close() marks shutdown while cleanup runs. Cleanup suppresses close failures and clears only the matching browser. Tests cover recovery, browser reuse, handler registration, and shutdown behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BrowserToPdfRenderer
  participant Browser
  participant Puppeteer
  Browser->>BrowserToPdfRenderer: report disconnection
  BrowserToPdfRenderer->>Browser: clean up disconnected browser
  BrowserToPdfRenderer->>Puppeteer: launch replacement browser
  Puppeteer-->>BrowserToPdfRenderer: return replacement browser
  BrowserToPdfRenderer-->>BrowserToPdfRenderer: share recovery promise
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: handling Puppeteer browser disconnects and relaunching the browser.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch puppeteer-reopen

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/binary/puppeteer/src/lib/render-to.ts`:
- Around line 33-40: The cleanupHandler registered in the render-to browser
lifecycle must not relaunch after an intentional shutdown. Update the close()
flow and cleanupHandler around this.launchedBrowser.on('disconnected') to track
intentional closing or disable the handler before this.launchedBrowser.close(),
while preserving recovery for unexpected disconnections; add a test verifying
close() does not invoke puppeteer.launch() again.
- Around line 33-40: Update the browser recovery logic around the cleanupHandler
and launchedBrowser lifecycle so shared renderers register only one disconnect
handler per browser, serialize concurrent recovery through a single in-flight
promise, and avoid duplicate cleanup/launch operations. Make cleanup
instance-safe by clearing launchedBrowser only when it still references the
browser being cleaned, while preserving browser() recovery behavior for
disconnected instances.
- Around line 33-36: Update the asynchronous cleanupHandler disconnect listener
to catch and handle rejections from cleanup or browser relaunch, ensuring
failures are not left as ignored Promise rejections. Keep the existing cleanup
and this.browser({ timeout }) recovery flow, but route errors through the
appropriate existing error-handling mechanism inside the callback.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f5043c50-51da-41d0-aae2-0be584c7e37e

📥 Commits

Reviewing files that changed from the base of the PR and between 50da9fb and 382470d.

📒 Files selected for processing (1)
  • libs/binary/puppeteer/src/lib/render-to.ts

Comment thread libs/binary/puppeteer/src/lib/render-to.ts Outdated
Comment thread libs/binary/puppeteer/src/lib/render-to.ts Outdated
Add a regression test to ensure `close()` does not trigger a new
Puppeteer launch after shutdown. Track an `isClosing` state and bypass
disconnect cleanup relaunch logic while closing, delegating `close()`
to `cleanup()` safely.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
libs/binary/puppeteer/src/lib/render-to.spec.ts (1)

45-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Invoke the registered disconnect handler in this test.

The test records disconnectedHandlers, but it never calls a handler. Lines 47-51 only test the synchronous activeBrowser.connected fallback in browser().

Invoke disconnectedHandlers[0]() and await recovery completion. This validates the event-driven recovery path and preserves coverage for the rejection handler at render-to.ts Lines 57-61.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/binary/puppeteer/src/lib/render-to.spec.ts` around lines 45 - 60, Update
the recovery test around the registered disconnectedHandlers to invoke
disconnectedHandlers[0]() and await its completion before asserting recovered
browsers. Preserve the existing activeBrowser.connected fallback assertions
while also exercising the event-driven recovery and its rejection handling in
renderer.browser().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/binary/puppeteer/src/lib/render-to.ts`:
- Around line 14-27: Update recoverBrowser and the browser launch path so
recovery does not reuse this.recoveringBrowser when browser() is called from the
current recovery operation. Use a non-reentrant launch path or track the browser
being recovered, ensuring an immediately disconnected replacement starts a new
recovery and returns a connected browser rather than creating a promise cycle.
- Around line 79-85: Update close() and the recovery flow around
recoveringBrowser so shutdown awaits any in-flight recovery before resolving.
Recheck isClosing after recovery cleanup and immediately before relaunching the
browser, preventing a recovery that was already in progress from launching after
close() completes. Add an interleaving test that pauses recovery during cleanup,
starts close(), and verifies no browser relaunch occurs after shutdown.

---

Nitpick comments:
In `@libs/binary/puppeteer/src/lib/render-to.spec.ts`:
- Around line 45-60: Update the recovery test around the registered
disconnectedHandlers to invoke disconnectedHandlers[0]() and await its
completion before asserting recovered browsers. Preserve the existing
activeBrowser.connected fallback assertions while also exercising the
event-driven recovery and its rejection handling in renderer.browser().
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e176ae4-ae47-4e29-b1a0-53cee61969bf

📥 Commits

Reviewing files that changed from the base of the PR and between 382470d and d452981.

📒 Files selected for processing (2)
  • libs/binary/puppeteer/src/lib/render-to.spec.ts
  • libs/binary/puppeteer/src/lib/render-to.ts

Comment thread libs/binary/puppeteer/src/lib/render-to.ts Outdated
Comment thread libs/binary/puppeteer/src/lib/render-to.ts
Add a regression test for close interleaving with recovery cleanup
and make shutdown wait for any in-flight recovery before and after
cleanup to avoid launching a new browser while closing.
Track the disconnected source browser for in-flight recovery so only
matching recoveries are reused. Start a fresh recovery when a
replacement
browser is already disconnected, and add a regression test for this
case.
@philiplehmann
philiplehmann merged commit 817fd88 into main Aug 10, 2026
6 checks passed
@philiplehmann
philiplehmann deleted the puppeteer-reopen branch August 10, 2026 17:06
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.

1 participant