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

Protocol error (Runtime.callFunctionOn): Target closed. #6610

Closed
mahnunchik opened this issue Nov 20, 2020 · 12 comments
Closed

Protocol error (Runtime.callFunctionOn): Target closed. #6610

mahnunchik opened this issue Nov 20, 2020 · 12 comments

Comments

@mahnunchik
Copy link

Steps to reproduce

Tell us about your environment:

  • Puppeteer version: 5.5.0
  • Platform / OS version: macOS 10.15.7
  • URLs (if applicable): -
  • Node.js version: v14.15.0

What steps will reproduce the problem?

Please include code that reproduces the issue.

  1. Open page
  2. Wait for non-existent selector
  3. Close page
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');

// not await!
page.waitForSelector('.non-existent').then(() => console.log('it should never happened));

await page.close();

What is the expected result?

Closing the page normally without errors.

What happens instead?

Error: Protocol error (Runtime.callFunctionOn): Target closed.
    at /app/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:208:63
    at new Promise (<anonymous>)
    at CDPSession.send (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:207:16)
    at ExecutionContext._evaluateInternal (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:200:50)
    at ExecutionContext.evaluateHandle (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/ExecutionContext.js:151:21)
    at WaitTask.rerun (/app/node_modules/puppeteer/lib/cjs/puppeteer/common/DOMWorld.js:528:37)
@marr
Copy link

marr commented Dec 4, 2020

@mahnunchik Why didn't you await the page.waitForSelector call? If you add await there, it should fix the issue.

@mahnunchik
Copy link
Author

@marr omitted await is important for the issue. It is simplified version of:

await Promise.race([
  page.waitForSelector('.existent').then(() => console.log('fire!')),
  page.waitForSelector('.non-existent').then(() => console.log('it should never happened')),
]);

await page.close();

When it is required to wait at least one element from set of elements.

@dpmott
Copy link

dpmott commented Mar 11, 2021

@marr still has a good point, in that you need know that each of those race operations are complete before closing the page out from under them.

Consider this:

  const operations = [ page.waitForSelector(...), page.waitForSelector(...), ];
  const winner = await promise.race(operations);
  // do something with the winner
  await Promise.all(operations); // wait for the rest of the operations to finish or time out
  await page.close();

It might also be possible to put a catch on each of your operations, so that their failure doesn't cause an error in your scripting:

  const operations = [ page.waitForSelector(...), page.waitForSelector(...), ];
  const winner = await promise.race(operations.map(op => op.catch(() => {})));
  // do something with the winner, but check to see if it's undefined first
  await page.close();

@taozuidesongshu
Copy link

taozuidesongshu commented Sep 2, 2021

The cause of the error has been found and the problem has been resolved
The specific reason is that the tab A you opened was opened when the business was not closed after running.
Then use target.close() to close tab A,
Because tab B and tab A have the same name as target, tab B is also closed by the way. Will explode this error
已找到错误原因,问题已解决
https://blog.csdn.net/Lifemustrough/article/details/119614026

@stale
Copy link

stale bot commented Jun 24, 2022

We're marking this issue as unconfirmed because it has not had recent activity and we weren't able to confirm it yet. It will be closed if no further activity occurs within the next 30 days.

@stale stale bot added the unconfirmed label Jun 24, 2022
@teoring
Copy link

teoring commented Jun 24, 2022

I reproduced this issue in v10.2.0.
Node v14.17.1

My code is:

` const browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--disable-setuid-sandbox', // Experimental [adding this 25/01 as it seems to resolve few known issues]
'--disable-dev-shm-usage', // Experimental [adding this 25/01 as it seems to resolve few known issues]
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
'--use-gl=egl',
'--disable-web-security',
'--proxy-server="direct://"', // Attempt to increase performance
'--proxy-bypass-list=*', // Attempt to increase performance
'--disable-features=IsolateOrigins,site-per-process', // Needed to enable iframes
'--lang=en-GB', // Experimental (to fix freezing)
'--ignore-certificate-errors', // Experimental (to fix freezing)
'--disable-accelerated-2d-canvas', // Experimental (to fix freezing)
'--disable-gpu' // Experimental (to fix freezing)
],
// '--start-fullscreen' ], // Experimental
headless: ! cliOptions.headful,
handleSIGINT: false,
slowMo: conf.d_delayPageActions,
});

            ....

            page.goto( sources[k].url ),
            page.waitForNavigation( { waitUntil: 'networkidle0' } ),
            page.waitForNavigation( { waitUntil: 'load' } )
            await page.waitForSelector( sources[k].accept_cookies_selector, { timeout: 20000, visible: true } );               

`

It was 100% reproducible, url: https://www.era.nl/huizenaanbod

    accept_cookies_selector: "body > div.cookiemelding2020 > div > div.cookieknoppenbar.col-12.col-md-3 > button.btn-success.cookieknop.mt-1",

waitForSelector was causing the behavior.

@stale stale bot removed the unconfirmed label Jun 24, 2022
@stale
Copy link

stale bot commented Aug 30, 2022

We're marking this issue as unconfirmed because it has not had recent activity and we weren't able to confirm it yet. It will be closed if no further activity occurs within the next 30 days.

@stale stale bot added the unconfirmed label Aug 30, 2022
@OrKoN
Copy link
Collaborator

OrKoN commented Sep 5, 2022

You should await the calls before you close the page. Otherwise, they will run on a closed target throwing the error.

@OrKoN OrKoN closed this as not planned Won't fix, can't repro, duplicate, stale Sep 5, 2022
legitnick pushed a commit to legitnick/vandepar that referenced this issue Feb 6, 2023
kind of stuck there problem mentioned on puppeteer/puppeteer#6610 ,found no solutions as of rn.
@AmirzhanovRenat
Copy link

vc

@stramanu
Copy link

stramanu commented May 29, 2023

To all those who come across this issue, I am sharing my own personal resolution:

(typescript)

class CPromise<T> {

	static promises: CPromise<any>[] = []
	static clearAll(name: string = null) {
		if (name == null) {
			for (const promise of this.promises) promise.clear();
		} else {
			for (const promise of this.promises.filter(p => p.name == name)) promise.clear();
		}
	}
	static $$<T>(promise: Promise<T>, name = '', ignoreCatch = true) {
		return new CPromise(promise, name, ignoreCatch).promise
	}

	promise: Promise<T>
	private cancelled = false

	constructor(
		public _promise: Promise<T>,
		public name = '',
		private ignoreCatch = false
	) {
		CPromise.promises.push(this)
		this.promise = new Promise((resolve, reject) => {
			_promise.then(a => {
				if (!this.cancelled) resolve(a)
			}).catch(e => {
				if (!this.cancelled && !ignoreCatch) reject(e)
			})
		})
	}

	clear() {
		this.cancelled = true
	}
}

const $$ = CPromise.$$

In this way, you can wrap all critical promises inside the CPromise.$$() function (or $$()) and cancel them before closing the page.
For example:

await $$(page.waitForFunction(() => document.querySelector('#test')), 'group-id')

// --------------------------

CPromise.clearAll('group-id')
await page.close()

@FotieMConstant
Copy link

I Just had this issue, to be honest i couldn't find a solution in any of these comments or elsewhere, so i managed to find my own way around. Here is an overview of my understanding and solution.

By the way my javascript solution is inspired by @stramanu's solution in typscript.

Why this error?

The error (TargetCloseError: Protocol error (Runtime.callFunctionOn): Target closed) is typically associated with attempting to interact with a target (page or context) that has been closed. Generally the script works well but yet fails(like in my case), so it's possible that there might be an asynchronous operation scheduled after the browser or page is closed.

Solution:

Ensure all promises are resolved, make sure that all promises are resolved before closing the browser or page. You can use Promise.all to await multiple promises simultaneously.

like in my case, i just had to wrap my call in a Promise.all

await Promise.all([
  // Your Puppeteer operations
]);

// Close the page
await page.close();

// Close the browser
await browser.close();

In simple words, the idea is to wrap whatever iteration you are doing in a promise and make sure it is all resolved before you close the page and browser.

Works like charm✨

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants