Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execution context was destroyed, most likely because of a navigation #3323

Closed
THE-GAME-YOU-LOST opened this issue Sep 30, 2018 · 21 comments
Closed

Comments

@THE-GAME-YOU-LOST
Copy link

  1. Puppeteer version: 1.8.0
  2. Platform / OS version: Ubuntu 16.04
  3. Node.js version: v10.10.0

What steps will reproduce the problem?
I have a website with unpredictable behaviour when a form is submitted, it can show me a message or it redirects me to a page, so I am currently checking for some messages, either in the redirected page or in main.

What is the expected result?
When redirected still check for an expected message without throwing me error.

What happens instead?
But instead I get an error "Execution context was destroyed, most likely because of a navigation", I tried using waitForNavigation, it worked but only when I had the page redirected. So now I am constantly checking for some possible messages and sometimes I get this error.

@aslushnikov
Copy link
Contributor

@THE-GAME-YOU-LOST this sounds bad.

Can you share a snippet that would reproduce this? I'm mostly interested what API call produces this error for you.

@THE-GAME-YOU-LOST
Copy link
Author

@aslushnikov I am doing something like shown in this snippet below:

const returnSearchResults = async (page) => {
    const pageSource = await page.content();
    if (/An error happened/.test(pageSource)) {
        return 'fail';
    }
    else if (/queries have been found/.test(pageSource)) {
        return 'success';
    }
    else if (/Timed out/.test(pageSource)) {
        return 'timeout';
    }
    else if (/Your searches are too fast/.test(pageSource)) {
        return 'too_fast';
    }
    else {
        return await returnSearchResults(page);
    }
}

const searchStep = async (page) => {
    await page.type('#searchbox input', 'Testing');
    await page.click('button[name="search"]');
    let searchResults = await returnSearchResults(page);
    if(searchResults === 'success') {
        console.log('success finding results');
    } else {
        console.log(searchResults);
    }
}


(async() => {
    const browser = await puppeteer.launch({
        args: [
            '--no-sandbox',
            '--disable-setuid-sandbox',
            '--disable-accelerated-2d-canvas',
            '--disable-gpu'
        ]
    });
    const page = await browser.newPage();
    await page.goto('http://snippet.example.com/', {waitUntil: 'domcontentloaded'});
    await searchStep(page).catch(e => {
        console.log('step searchStep exception ' + e);
        process.exit(1);
    });
    await page.close();
    await browser.close();
})();

As you can see returnSearchResults is constantly called if no result is found. It fails on page.content() if there is a redirect, a expected result can be always one of them but only one redirects, which is the "too_fast"

Also sorry if I do bad javascript approach, I am still beginning, thank you!

@aslushnikov
Copy link
Contributor

@THE-GAME-YOU-LOST I see. The right thing to do would be to await navigation caused by page.click. Since you don't know if there's any navigation happenning - is there any selector you can use that appears after the successful click? If yes, you can do page.waitForSelector to make sure the page is in consistent state.

@THE-GAME-YOU-LOST
Copy link
Author

Yes, it resolved the problem, thanks!

@RebliNk17
Copy link

RebliNk17 commented Dec 2, 2018

@aslushnikov , I'm having the same issue when trying to open a page that has (probably?!) JavaScript redirection.

The same code works for every other page that doesn't have JavaScript redirection.

If I'm adding await page.waitForNavigation(); then the problematic page is working - but the other pages are waiting for navigation even if page stopped/finished loading...

I don't know what selector or code causing the redirection.

I also can't tell if a redirection happened from the page.goto() so I will know that I need to use the page.waitForNavigation()

Edit:
I have found that for at least one website the redirection is happening from window.location
But it is vary!

@tomgallagher
Copy link

I've just hit the same problem.

If I hit a page which has a javascript redirect on it, with a normal page.goto, then I get a "Execution context was destroyed, most likely because of a navigation" error.

If I add

currentPage.waitForNavigation({timeout: timeout, waitUntil: ['domcontentloaded', waitUntil]})

then this sorts out the pages with javascript redirects on them. But then pages without javascript redirects offer me a navigation timeout error.

It would be useful to have something like a followLocation: true flag on the page.goto options, so this is handled gracefully by Puppeteer.

@aslushnikov
Copy link
Contributor

@tomgallagher @RebliNk17 do you guys know a URL where you want to land after the page.goto call and all subsequent js redirects?

@RebliNk17
Copy link

@aslushnikov , Yes.
E.g:
http://pit.evertop.pl/
I am maintaining a crawler and i have few others, but I will not to post them here (lots of them are adults for some reason...)

@tomgallagher
Copy link

Mine are geolocation redirects so I probably can't usefully add anything. What I am doing now is catching the page.goto error and returning it, then using the error message string to test if it includes "Execution context was destroyed" to resend the page with a marker for js redirect. This enables me to add in the currentPage.waitForNavigation on the second go around and everything is fine. It's a bit messy and I hope you guys have a policy of not changing the syntax of your error messages!! But happy to paste the code here if that would help.

@aslushnikov
Copy link
Contributor

@RebliNk17 @tomgallagher well, yes, looks like we need something like #3627

@adgonzalez-ml
Copy link

Having the same issue with automatic redirects

this is an example url http://ramdileo.com/mercado/
in my case I need to know if a redirect is made, but Im not waiting for all pages to redirect to another page (using this for an automatic process that navigates to thousands of pages)

@Moshisho
Copy link

Moshisho commented Jan 6, 2019

I've added the following code to MyPuppeteer helper which solved it for me:

async clickRedirect(element) {
    const page = this.helpers['Puppeteer'].page;
    await page.waitForSelector(element);
    return await page.click(element);
}

@xctest
Copy link

xctest commented Jan 17, 2019

Hi, this is my code, hope it can have a little help to you

    //check if the page redirects
    let url_redirected = false;
    page.on('response', response => {
        const status = response.status()
        //[301, 302, 303, 307, 308]
        if ((status >= 300) && (status <= 399)) {
            url_redirected = true;
        }
    });

    //goto page
    await page.goto('your_url', {
        timeout: 0,
        waitUntil: 'networkidle2'
    });
    if (url_redirected) {
       //if page redireced , we wait for navigation end
        await page.waitForNavigation({
            waitUntil: 'domcontentloaded'
        })            
    };

    //finally , we remove listeners in case the response event fire more than once
    page.removeAllListeners('response');

@janbiedermann
Copy link

janbiedermann commented Mar 6, 2019

none of those things above work reliably for me. Either tests are way to slow, or fail with "Execution context was destroyed"
Using the @xctest way, sometimes listeners get removed before the onresponse was triggered.

Instead I would like to suggest something like:
FrameManager.js:

  async waitForFrameNavigation(frame, options = {}) {
    assertNoLegacyNavigationOptions(options);
    const {
      waitUntil = ['load'],
      timeout = this._timeoutSettings.navigationTimeout(),
      noNavEvTimeout = 250ms; // <---- pass in options
    } = options;
    const watcher = new LifecycleWatcher(this, frame, waitUntil, timeout, noNavEvTimeout);
    const error = await Promise.race([
      watcher.noNavigationOccuredPromise(),  // <---- here
      watcher.timeoutOrTerminationPromise(),
      watcher.sameDocumentNavigationPromise(),
      watcher.newDocumentNavigationPromise()
    ]);
    watcher.dispose();
    if (error)
      throw error;
    return watcher.navigationResponse();
  }

where watcher.noNavigationOccuredPromise() would resolve if within noNavEvTimeout nothing happened, otherwise, if a navigation was triggered within noNavEvTimeout, watcher.noNavigationOccuredPromise() would do nothing and one of the other 3 would trigger as before, so if a navigation happened, page will be loaded normally. Also only if a navigation happened, it will wait a max of navigationTimeout().

@janbiedermann
Copy link

alternative, working for me, without the need to change puppeteer:

            var response_event_occurred = false;
            var page = CurrentPage;
            var response_handler = function(event){ response_event_occurred = true; };
            
            var response_watcher = new Promise(function(resolve, reject){
              setTimeout(function(){
                if (!response_event_occurred) {
                  resolve(true); 
                } else {
                  setTimeout(function(){ resolve(true); }, 30000);
                }
                page.removeListener('response', response_handler);
              }, 500);
            });
            
            page.on('response', response_handler);

            var navigation_watcher = page.waitForNavigation();

            await elementhandle.click({button: "left"});

            await Promise.race([response_watcher, navigation_watcher]);

@carlfost
Copy link

I am getting the same error using Puppeteer "version": "1.13.0".
ERROR: Execution context was destroyed, most likely because of a navigation.
Anyone has a link to the reason for this kind of error? Or is there a solution?

Code sample:
let lastDate = new Date("2019-01-01");
for(carditem of carditemlist) {
try {
testdate = await carditem.$eval('div dl dd time span.date', el => el.innerText);
let webDate = new Date(testdate);
webDate.setDate(webDate.getDate());
if(lastDate < webDate) {
// check if test dates are different
if(oldTestDate.getTime() !== webDate.getTime()) {
const navigationPromise = page.waitForNavigation();
const resultsel = ".consumer-card:nth-child(1) > .labs-list > .consumer-card-item:nth-child(1) > .margin-sides > a";
await page.waitForSelector(resultsel);
await page.click(resultsel);
// other non-nagivation codes
await page.goBack({waitUntil : "networkidle0"});
await navigationPromise;

              // GETTING THE ERROR AT THIS POINT
              testname = await carditem.$eval('div.small-heading bdi', el => el.innerText);
              //Execution context was destroyed, most likely because of a navigation.
          }
        }
  }catch (e) {
        let msg = e.message;
  }

}

@LuizAsFight
Copy link

used @janbiedermann 's code. Just pointing out that I've used part of @xctest 's code too. Also applied ES6 patterns, removing var and function. Code:

`
const MAX_WAITING_TIME_ACCESS_URL = 30000;
const page = await browser.newPage();

await page.goto(url, {
  timeout: 0,
  waitUntil: 'networkidle2'
});

let responseEventOccurred = false;
const responseHandler = (event) => (responseEventOccurred = true);

const responseWatcher = new Promise(function(resolve, reject){
  setTimeout(() => {
    if (!responseEventOccurred) {
      resolve(); 
    } else {
      setTimeout(() => resolve(), MAX_WAITING_TIME_ACCESS_URL);
    }
    page.removeListener('response', responseHandler);
  }, 500);
});

page.on('response', responseHandler);


await Promise.race([
  responseWatcher,
  page.waitForNavigation()
]);

`

Thank you guys, looks like it's working

@vishalkumargaurav
Copy link

I have also got same problem as Execution context was destroyed, most likely because of a navigation.

I have just increase wait time and it fine work . It happend mostly due to slow network.

@grahaml
Copy link

grahaml commented Mar 18, 2020

@THE-GAME-YOU-LOST dammit I just lost the game, and I've been winning for a solid decade.

@gilad905
Copy link

gilad905 commented Aug 20, 2020

I'm using page.waitForSelector("html") for that case, I find it the easiest and most reliable.

@segun-flexible
Copy link

Hi, this is my code, hope it can have a little help to you

    //check if the page redirects
    let url_redirected = false;
    page.on('response', response => {
        const status = response.status()
        //[301, 302, 303, 307, 308]
        if ((status >= 300) && (status <= 399)) {
            url_redirected = true;
        }
    });

    //goto page
    await page.goto('your_url', {
        timeout: 0,
        waitUntil: 'networkidle2'
    });
    if (url_redirected) {
       //if page redireced , we wait for navigation end
        await page.waitForNavigation({
            waitUntil: 'domcontentloaded'
        })            
    };

    //finally , we remove listeners in case the response event fire more than once
    page.removeAllListeners('response');

Thanks This Fix My Problem
Am trying to crawl a website built with Reactjs, but it redirecting on page load.
And this techniques fix it

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

No branches or pull requests