10

So I used a while loop so my test will run in a constant loop until my back-end will crash. I've implemented a try and catch(error) so any front-end crash the automation will refresh and keep running

while(true){
try{
    await page.waitFor(selector)
    await page.click(selector)    
}
catch(error){
    console.log("FE crashed with\n\n" + error + "\n\nRefreshing page and continuing profile switching")
    await page.reload(page);
    continue;
}}

So indeed any timeout error the automation refreshes the page and continuing the loop. but Im recieving a different crash error

(node:6535) UnhandledPromiseRejectionWarning: Error: Page crashed!
at Page._onTargetCrashed (/home/raymond/node_modules/puppeteer/lib/Page.js:170:24)
at CDPSession.Page.client.on.event (/home/raymond/node_modules/puppeteer/lib/Page.js:125:56)
at CDPSession.emit (events.js:182:13)
at CDPSession._onMessage (/home/raymond/node_modules/puppeteer/lib/Connection.js:200:12)
at Connection._onMessage (/home/raymond/node_modules/puppeteer/lib/Connection.js:112:17)
at _tickCallback (internal/process/next_tick.js:43:7)
at listOnTimeout (timers.js:294:7)
at processTimers (timers.js:268:5)
(node:6535) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)

How can I handle this error?, if I refresh the page manually everything works fine. Thanks

Dan Raymond
  • 143
  • 1
  • 6
  • I looks like you puppeteer "page" is crashing on the "catch" portion, and you don't have another "try...catch" inside it. Try to handle this error. Sometimes puppeteer itself crashes, chromium crashes, etc... so you have to handle those errors too. For example, you can close the page (or browser) and create another page (or browser). – lcrespilho Sep 16 '19 at 12:24
  • Ok, I can try this, but how can I know the system crashed, I mean my try and catch didnt work, so try and catch the entire loop? – Dan Raymond Sep 16 '19 at 13:01
  • I would to try...catch inside the catch, just to issue the "await page.reload(page)" command, since I think it is causing the issue. But try...catch the while() with the browser creation can be better. – lcrespilho Sep 16 '19 at 14:48
  • I tried try and catch the while loop didnt solve the problem I will try what you suggested – Dan Raymond Sep 16 '19 at 15:44
  • 1
    You want `await page.reload();`, passing page as an option doesnt make sense. You may want to have a look for [reload docs and options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagereloadoptions) – Maurice Meyer Sep 16 '19 at 16:44

2 Answers2

19

You are assuming, that the error happened because the page navigation failed. This might be the case, but it could also be a different error like a Protocol error. In a case like that, you cannot just reuse the page object, but you first have to restart the browser.

Some time ago, I crawled roughly 400k pages for testing purposes. In total, I experienced 34 of these puppeteer crashes, where some unexpected error crashes the whole browser. To harden your code against these kind of crashes, you need a robust way to restart the browser.

Code Sample

let browser = await puppeteer.launch(/* .. */);
let page = await browser.newPage();

while(true) {
    try {
        // your code that might crash
    } catch (err) {
        try {
            await page.reload(); // soft fix
        } catch (recoveringErr) {
            // unable to reload the page, hard fix
            try {
                await browser.close();
            } catch (err) { // browser close was not necessary
                // you might want to log this error
            }
            browser = await puppeteer.launch(/* .. */);
            page = await browser.newPage();
        }
    }
}

Although this code might look horrendous with three nested try..catch blocks, it does a good job of keeping your code running.

First, your original code is executed. If the initial problem happens, a page.reload call is tried to fix the problem. In case this works, the loop will continue running. If that does not work, the browser will be restarted.

For restarting the browser, I recommend to first try to close the old browser. Although, this might likely fail, it clears all memory and correctly disposes the browser object. Depending on your use case, you might want to log the error or simply ignore it. After disposing the old browser object, you can restart the browser and the loop may continue.

Alternative

As an alternative, you could use my library puppeteer-cluster which has error handling build in. The library automatically restarts the browser in cases in which the page is not usable anymore. In addition, the library can run multiple pages in parallel (as you are trying to stress-test the server I'm assuming).

Thomas Dondorf
  • 23,416
  • 6
  • 84
  • 105
1

As per the official documentation, an 'error' event is emitted when the page crashes which can be used to do certain stuff basis the application.

page.on('error', err => { /*custom logic to handle the crash*/ });

For the above specific use case, you could go about doing something like this:

let browser = await puppeteer.launch();
let page = await getNewPage(browser);

while(true){
        try{
            if (page.isClosed()) {
              page = await getNewPage(browser);
            }
            await page.waitFor(selector)
            await page.click(selector)    
        }
        catch(error){
            console.log("FE error with\n\n" + error + "\n\nRefreshing page and continuing profile switching")
            await page.reload();
            continue;
        }}


async function getNewPage(browser) {
    let page = await browser.newPage();
    page.on('error', err => {
        if (!page.isClosed()) {
            //Close page if not closed already
            page.close();
        }
    }
    return page;
}

Reference: page.on('error')

Kunal Kukreja
  • 737
  • 4
  • 18