puppeteer: EventEmitter memory leak detected. 11 exit listeners added

This is the full error message: MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit

I run Node v8.1.2 with tip-of-tree Puppeteer

The code is written with Promise.all, but even if I run work() sequentially the same error appears.

const puppeteer = require('puppeteer')
const R = require('rambda')

const resolution = {
  x : 1920,
  y : 1080,
}

const args = [
  '--disable-gpu',
  `--window-size=${ resolution.x },${ resolution.y }`,
  '--no-sandbox',
]

const work = async () => {
  const browser = await puppeteer.launch({
    headless     : true,
    handleSIGINT : false,
    args         : args,
  })
  const page = await browser.newPage()

  const url = 'https://ilearnsmarter.com/learning-meme'

  await page.setViewport({
    width  : resolution.x,
    height : resolution.y,
  })

  await page.goto(url, { waitUntil : 'networkidle' })
  await browser.close()
}

const fn = async () => {
  const promised = R.range(0, 12).map(() => work())
  await Promise.all(promised)

  console.log('DONE')
}

fn()

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 18 (5 by maintainers)

Most upvoted comments

@selfrefactor ah, indeed, you launch chrome processes in parallel. Every chrome instance adds a listener to the process’s “exit” event to cleanup properly. 12 chrome instances add 12 listeners, which yields the warning.

You can fix this by adding process.setMaxListeners(Infinity) in the very beginning of your script:

const puppeteer = require('puppeteer')
const R = require('rambda')

process.setMaxListeners(Infinity); // <== Important line

const resolution = {
  x : 1920,
  y : 1080,
}
// ...

Note: you don’t need to launch browser just to create a page. Instead, you can open multiple pages in the same browser:

const puppeteer = require('puppeteer')
const R = require('rambda')

const resolution = {
  x : 1920,
  y : 1080,
}

const args = [
  '--disable-gpu',
  `--window-size=${ resolution.x },${ resolution.y }`,
  '--no-sandbox',
]

let browser;

const work = async () => {
  const page = await browser.newPage()

  const url = 'https://ilearnsmarter.com/learning-meme'

  await page.setViewport({
    width  : resolution.x,
    height : resolution.y,
  })

  await page.goto(url, { waitUntil : 'networkidle' })
  await page.close();
}

const fn = async () => {
  browser = await puppeteer.launch({
    headless     : true,
    handleSIGINT : false,
    args         : args,
  })
  const promised = R.range(0, 12).map(() => work())
  await Promise.all(promised)
  browser.close()

  console.log('DONE')
}

fn()

right before on your app.listen(port,() => {}) add process.setMaxListeners(0) for infinity event listener chaining

Lighthouse and chrome-launcher handled this in https://github.com/GoogleChrome/lighthouse/pull/2959.

@domarmstrong the reason your script doesn’t work with tip-of-tree puppeteer is because you don’t close browsers if exception is thrown. This is a memory leak.

The following script works just fine:

const puppeteer = require('puppeteer');

// bad url with no protocol fails fast
const jobs = [...new Array(12)].map(() => 'google.com');

const work = async (url) => {
  const browser = await puppeteer.launch({dumpio: true});
  try {
    const page = await browser.newPage();
    await page.goto(url);
  } catch (err) {
    console.log(err.message);
  } finally {
    browser.close();
    if (jobs.length) {
      work(jobs.pop());
    } else {
      process.exit();
    }
  }
};

work(jobs.pop());

if you pass https://google.com instead of google.com, no exception will be thrown and your original script will work just fine.

(node:25120) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit