Puppeteer: How to verify if the browser instance is active and functional?

I am attempting to maintain multiple tabs within a single instance of a browser. Once the tabs have finished their tasks, I close them and re-open them every few seconds without closing the main browser. This way, I don’t need to log in again for each tab during every iteration.

The goal is to keep the browser open while managing the tabs. Here’s a simplified version of my code; please disregard any syntax issues:

let activeBrowser = null;
async function startPuppeteer(configurations) {
    if (!activeBrowser) {
        activeBrowser = await puppeteer.launch({ headless: false, args: ['--no-sandbox'] });
    }

    configurations.forEach(config => {
        openNewTab(config);
    });
}

async function openNewTab(config) {
    const newPage = await activeBrowser.newPage();
    // perform actions
    newPage.close();
}

setInterval(() => {
    startPuppeteer(configurations);
}, 50000);

The challenge I’m facing is that the browser may crash or close unexpectedly, but the activeBrowser still exists as an instance of Puppeteer. Consequently, opening a new tab with that instance fails, resulting in an error:

(node:5720) UnhandledPromiseRejectionWarning: Error: WebSocket is not open: readyState 3 (CLOSED)

My question is: how can I confirm that I have a working instance of Puppeteer in activeBrowser? I want to be able to create and replace the instance if the previous one has been closed.

To check if your activeBrowser instance is still working, you can attempt creating a dummy page and handle any exceptions that occur if the instance is invalid. Here's a quick way to do it:

async function isBrowserActive() { try { const page = await activeBrowser.newPage(); await page.close(); return true; } catch (error) { return false; } }

async function startPuppeteer(configurations) {
if (!activeBrowser || !(await isBrowserActive())) {
activeBrowser = await puppeteer.launch({ headless: false, args: [‘–no-sandbox’] });
}

configurations.forEach(config => {
    openNewTab(config);
});

}

This adds a simple check to ensure your browser instance is valid before proceeding with opening tabs.

To verify if your activeBrowser instance is still active and functional, you can implement a combination of checking for its state by creating a dummy page, handling exceptions, and using Puppeteer's events for monitoring. This ensures you efficiently manage the browser lifecycle.

Here's how you can set up a robust solution:

let activeBrowser = null;

async function startPuppeteer(configurations) {
if (!activeBrowser || !(await isBrowserActive())) {
activeBrowser = await puppeteer.launch({ headless: false, args: [‘–no-sandbox’] });
activeBrowser.on(‘disconnected’, handleBrowserDisconnect);
}

configurations.forEach(config => {
    openNewTab(config);
});

}

async function isBrowserActive() {
try {
const page = await activeBrowser.newPage();
await page.close();
return true;
} catch (error) {
return false;
}
}

function handleBrowserDisconnect() {
console.log(‘Browser disconnected. Restarting…’);
activeBrowser = null;
// Optional: Immediately restart the browser or log information for review
}

async function openNewTab(config) {
try {
const newPage = await activeBrowser.newPage();
// perform actions
await newPage.close();
} catch (error) {
console.error(‘Error opening new page:’, error);
}
}

setInterval(() => {
startPuppeteer(configurations);
}, 50000);

By combining these approaches, you can maintain a smooth operational flow of your automated tasks, ensuring you're prepared for unexpected browser disconnections. This setup optimizes resource usage and minimizes downtime.

In addition to checking the functionality of the activeBrowser instance by opening a dummy page, another approach is to leverage Puppeteer's built-in events to detect when the browser has closed unexpectedly. This method allows you to monitor the browser's lifecycle directly, and restart it if needed.

Here's how you can enhance your solution using Puppeteer events:

let activeBrowser = null;

async function startPuppeteer(configurations) {
if (!activeBrowser || !isBrowserActive()) {
activeBrowser = await puppeteer.launch({ headless: false, args: [‘–no-sandbox’] });
activeBrowser.on(‘disconnected’, handleBrowserDisconnect);
}

configurations.forEach(config => {
    openNewTab(config);
});

}

function isBrowserActive() {
return !activeBrowser._closed; // Puppeteer doesn’t expose a direct property, but _closed works
}

function handleBrowserDisconnect() {
console.log(‘Browser disconnected. Attempting to restart.’);
activeBrowser = null;
// Add logic here to handle restarting the browser if necessary
}

async function openNewTab(config) {
try {
const newPage = await activeBrowser.newPage();
// perform actions
await newPage.close();
} catch (error) {
console.error(‘Failed to open a new page:’, error);
// Additional error handling can be placed here
}
}

setInterval(() => {
startPuppeteer(configurations);
}, 50000);

This implementation leverages the disconnected event on the browser instance, notifying your application whenever the browser disconnects unexpectedly. This way, you can efficiently handle browser restarts or any related cleanup. Additionally, checking the internal property _closed can help determine if the browser has been closed, although this is more of an internal check and might change with Puppeteer updates.

In addition to the existing solutions for maintaining an operational activeBrowser instance, consider incorporating a systematic reconnection strategy. This adds an extra layer of redundancy, ensuring that your application properly handles disconnections without user intervention. Here's a method that combines a reconnection approach with adaptive error handling:

let activeBrowser = null; let reconnectAttempts = 0; const maxReconnectAttempts = 5;

async function startPuppeteer(configurations) {
if (!activeBrowser || !(await isBrowserFunctional())) {
await launchBrowser();
}

// Reset the reconnection attempts on a successful operation
reconnectAttempts = 0;

configurations.forEach(async (config) => {
    await openNewTab(config);
});

}

async function launchBrowser() {
try {
activeBrowser = await puppeteer.launch({ headless: false, args: [‘–no-sandbox’] });
activeBrowser.on(‘disconnected’, handleBrowserDisconnect);
} catch (error) {
console.error(‘Failed to launch browser:’, error);
}
}

async function isBrowserFunctional() {
try {
const page = await activeBrowser.newPage();
await page.close();
return true;
} catch (error) {
return false;
}
}

function handleBrowserDisconnect() {
console.warn(‘Browser disconnected. Attempting to reconnect…’);
activeBrowser = null;
reconnectBrowser();
}

async function reconnectBrowser() {
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
await launchBrowser();
} else {
console.error(‘Max reconnection attempts reached. Please check browser availability or network issues.’);
}
}

async function openNewTab(config) {
try {
const newPage = await activeBrowser.newPage();
// perform actions
await newPage.close();
} catch (error) {
console.error(‘Error opening new page:’, error);
// Implement additional error handling or retry mechanisms
}
}

setInterval(() => {
startPuppeteer(configurations);
}, 50000);

This method sets a limit to reconnection attempts and ensures each attempt is properly logged. It maintains operational efficiency by limiting unnecessary resource consumption or long-term execution on failure, providing a robust browser management solution for your Puppeteer application.

To verify if the activeBrowser instance is still functional, try creating a dummy page and handle any errors. Here's a simple method:

async function isBrowserActive() { try { const page = await activeBrowser.newPage(); await page.close(); return true; } catch (error) { return false; } }

async function startPuppeteer(configurations) {
if (!activeBrowser || !(await isBrowserActive())) {
activeBrowser = await puppeteer.launch({ headless: false, args: [‘–no-sandbox’] });
activeBrowser.on(‘disconnected’, handleBrowserDisconnect);
}

configurations.forEach(config => {
    openNewTab(config);
});

}

function handleBrowserDisconnect() {
console.log(‘Browser disconnected. Restarting…’);
activeBrowser = null;
}

This ensures your browser instance is valid before proceeding with tasks, keeping your setup reliable.