puppeteer: Puppeteer doesn't run under WSL (Windows subsystem for Linux)

On Ubuntu 16.04.3 LTS via WSL (Linux pc-name 4.4.0-43-Microsoft #1-Microsoft Wed Dec 31 14:42:53 PST 2014 x86_64 x86_64 x86_64 GNU/Linux).

Gives this error:

ERROR { Error: kill ESRCH
    at Object._errnoException (util.js:1024:11)
    at process.kill (internal/process.js:183:18)
    at forceKillChrome (/mnt/c/Users/me/code/node_modules/puppeteer/lib/Launcher.js:169:19)
    at Function.launch (/mnt/c/Users/me/code/node_modules/puppeteer/lib/Launcher.js:144:7)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7) code: 'ESRCH', errno: 'ESRCH', syscall: 'kill' }
ERROR { Error: kill ESRCH
    at Object._errnoException (util.js:1024:11)
    at process.kill (internal/process.js:183:18)
    at forceKillChrome (/mnt/c/Users/me/code/node_modules/puppeteer/lib/Launcher.js:169:19)
    at Function.launch (/mnt/c/Users/me/code/node_modules/puppeteer/lib/Launcher.js:144:7)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7) code: 'ESRCH', errno: 'ESRCH', syscall: 'kill' }

Have tried:

sudo apt install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget

… which doesn’t help.

Only other mention I can find of Puppeteer on WSL is https://github.com/GoogleChrome/puppeteer/issues/290#issuecomment-322964612.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 43
  • Comments: 63

Commits related to this issue

Most upvoted comments

This works for me:

const browser = await puppeteer.launch({
       args: [
            '--disable-gpu',
            '--disable-dev-shm-usage',
            '--disable-setuid-sandbox',
            '--no-first-run',
            '--no-sandbox',
            '--no-zygote',
            '--single-process',
       ]
  })

The following works for me in latest WSL:

npm install puppeteer
sudo apt-get install chromium-browser

const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch({executablePath: '/usr/bin/chromium-browser'});
  console.log(await browser.version());
  const page = await browser.newPage();
  await page.goto('https://google.com');
  await page.screenshot({path: 'google.png'});
  await browser.close();
})();

I evaluated Windows linux Subsystem for Frontend Development and, 5/2020, it appears far from ready. Back to OSX! there’s still work to do guys, good luck!

If anyone’s having trouble, as an alternative option, letting it control Windows Chrome/Chromium seems to work fine (for me at least).

I added my Chrome directory to my PATH on the Windows side, it automatically gets translated/appended to PATH on the WSL side, so after that it’s literally just:

const browser = await puppeteer.launch({ executablePath: 'chrome.exe' })

And this way you actually have the option of running it non-headless without having to run a window server, install a desktop environment, etc.

Obviously try and make sure you’re using a compatible version.

Quick tip, if you run node --experimental-repl-await (Node 10) you can play around in a Node REPL with top level await (no need for a wrapping function), which makes testing this stuff a whole lot easier/more fun.

Edit: there is at least one caveat with this method, see below.

const browser = await puppeteer.launch({
    args: ['--no-sandbox', '--disable-setuid-sandbox']
});

It was mentioned in different ways before by a few users, but somehow got lost in the thread.

This is what worked for me on WSL2 running an Ubuntu 20.04:

  1. Install Chrome. Here’s a guide.
  2. Run $ which google-chrome to check where it was installed. Probably /bin/google-chrome.
  3. Use const browser = await puppeteer.launch({ executablePath: "/bin/google-chrome" });

Thanks to all in this thread for hits!

I managed to run headless: false with X-server. It does not require for any extra args and runs chromium embedded to puppeteer package. I means I don’t need any extra code to handle WSL2, I can run same script that work in windows and desktop ubuntu.

Here is a step by step from clean ubuntu in wsl2 to opened chromium window:

  1. install fresh ubuntu 18.04 with WSL 2
  2. Install VcXsrc
    1. chose the options “Multiple windows” and “Start no client”
    2. On the page that lets you enable extra settings, be sure to disable access control. By default it only allows the local IP 127.0.0.1. Since WSL has its own IP address, which changes often, allow connections from all clients
  3. from this blog-post run following steps in ubuntu:
    1. sudo apt update
    2. sudo apt upgrade
    3. sudo visudo -f /etc/sudoers.d/dbus - A Nano editor will launch
    4. Enter the following line, where <your_username> is replaced by your username <your_username> ALL = (root) NOPASSWD: /etc/init.d/dbus ctrl+X to close, and agree to save changes
    5. In your .bashrc (or .zshrc if you are using ZSH)
      # set DISPLAY variable to the IP automatically assigned to WSL2
      export  DISPLAY=$(cat /etc/resolv.conf |  grep nameserver |  awk  '{print $2; exit;}'):0.0
      sudo /etc/init.d/dbus start &> /dev/null
      
    6. source ~/.bashrc - apply changes
  4. sudo apt-get install for all packages from here https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix
  5. install nvm and node v14.3.0 or later
  6. mkdir pup-wsl2 && cd pup-wsl2
  7. npm init -y
  8. npm install puppeteer
  9. node --experimental-repl-await
  10. const b = await require('puppeteer').launch({ headless: false }); (in REPL)

Now you have opened chromium window. You can continue play with it in REPL (const p = await b.newPage(); await p.goto('https://example.com');), or close REPL and run you scripts.

I was running into the same problem with newPage() in headless mode (but not with headless: false). What seems to fix it for me is setting userDataDir in the launch options. Be aware that this will be a Windows path, not a WSL one, because it gets passed to the browser as an argument. If you don’t specify this, by default it tries to create a profile directory at /tmp/puppeteer_dev_profile-<randomized string>, which actually results in it being created in C:\tmp\... - maybe that causes an issue because of permissions or something.

The other issue I ran into is that this directory never gets cleaned up like it should when you close the browser, whether you manually specify userDataDir or not (you should check out C:\tmp as already mentioned because there’s likely a whole bunch of useless folders in there). You can use a module like rimraf or fs-extra to remove it manually instead.

Example:

const puppeteer = require('puppeteer');
const rimraf = require('rimraf');

const USER_DATA_DIR = 'D:\\temp\\puppeteer_user_data';
const USER_DATA_DIR_WSL = '/mnt/d/temp/puppeteer_user_data';

(async () => {
  const browser = await puppeteer.launch({
    executablePath: 'chrome.exe',
    userDataDir: USER_DATA_DIR
  });

  ...

  await browser.close();
})().finally(() => rimraf(USER_DATA_DIR_WSL));

Maybe this is something that can be fixed in the library with a little WSL detection magic.

I’m using WSL2 and this is what worked for me! Follow these steps from Microsoft to install Chrome on WSL.

  1. Change directories into the temp folder: cd /tmp
  2. Use wget to download it: sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
  3. Get the current stable version: sudo dpkg -i google-chrome-stable_current_amd64.deb
  4. Fix the package: sudo apt install --fix-broken -y
  5. Configure the package: sudo dpkg -i google-chrome-stable_current_amd64.deb

If you are able to execute google-chrome from terminal, then your code should work

The quickest solution for me was to install the packages listed below. After that puppeteer runs out of the box, without additional launch options for the browser. sudo apt install libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2

My current configuration for a working project: [ “–disable-gpu”, “–renderer”, “–no-sandbox”, “–no-service-autorun”, “–no-experiments”, “–no-default-browser-check”, “–disable-dev-shm-usage”, “–disable-setuid-sandbox”, “–no-first-run”, “–no-zygote”, “–single-process”, “–disable-extensions” ]

It was mostly selective trail and error until I got this one working.

As I took a while to find the instructions, I am posting here:

If you want to use Puppetter on WSL2 with {headless: false} (so, to open the Chrome natively), the easiest way is to install VcXsrc on your Windows.

Step by step you can find it here (just ignore the part on what he says about Cypress)

Thanks all

['--no-sandbox', '--disable-setuid-sandbox']

was our winner

beforeEach(async () => {
    browser = await puppeteer.launch({
        headless: true,
        ignoreDefaultArgs: ['--disable-extensions'],
        args: [
            // '--disable-gpu',
            // '--disable-dev-shm-usage',
            // '--disable-setuid-sandbox', //default
            // '--no-first-run',
            '--no-sandbox', // default
            // '--no-zygote',
            '--single-process',
        ]
    });

    page = await browser.newPage();

    await page.setViewport({ width: 900, height: 600 });
    await page.goto('http://localhost:3000/');
    await page.waitForSelector(pageObjects.navFactory);
});
  • windows 10
  • wsl
  • visual studio code

I’ve got it working on WSL2 with Ubuntu 20.04 using chromium:

1 - Follow the instructions here to install chromium from Debian Buster’s repos. For the /etc/apt/sources.list.d/debian.list file, use the following:

deb http://ftp.debian.org/debian buster main
deb http://ftp.debian.org/debian buster-updates main
deb http://security.debian.org/debian-security buster/updates main contrib non-free

And for /etc/apt/preferences.d/chromium.pref:

# Note: 2 blank lines are required between entries
Package: *
Pin: release a=focal
Pin-Priority: 500

Package: *
Pin: origin "ftp.debian.org"
Pin-Priority: 300

# Pattern includes 'chromium', 'chromium-browser' and similarly
# named dependencies:
Package: chromium*
Pin: origin "ftp.debian.org"
Pin-Priority: 700

2 - Follow @junminstorage 's instructions above: https://github.com/puppeteer/puppeteer/issues/1837#issuecomment-405047723, replacing const browser = await puppeteer.launch({executablePath: '/usr/bin/chromium-browser'}); with const browser = await puppeteer.launch({executablePath: '/usr/bin/chromium'});

Just tested in 1803, seems to work on this build (only tested on project though).

It just hangs for me if I try setting "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe" as the executablePath unless I also add userDataDir. So, final solution to get it to run on both Windows and WSL is:

#!/usr/bin/env node
const puppeteer = require('puppeteer-core');
const rimraf = require('del');

const PATHS = {
    win32: {
        executablePath: "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
        userDataDir: 'C:\\Users\\<USERNAME>\\AppData\\Local\\Temp\\puppeteer_user_data',
    },
    linux: {
        executablePath: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
        userDataDir: '/mnt/c/Users/<USERNAME>/AppData/Local/Temp/puppeteer_user_data',
    },
}

async function main() {
    const browser = await puppeteer.launch({
        executablePath: PATHS[process.platform].executablePath,
        userDataDir: PATHS.win32.userDataDir,
        headless: false,
    });
    const page = await browser.newPage();
    await page.goto('https://example.org');
    await page.screenshot({path: `${__dirname}/screenshots/login.png`});

    await browser.close();
}

main().finally(async () => {
    await rimraf(PATHS[process.platform].userDataDir, {force: true})
}).catch(err => {
    console.error(err);
    process.exit(1);
});

I also couldn’t get chromium-browser to work, but if you want to give it a whirl, here’s some copy-pasta:

sudo add-apt-repository ppa:chromium-team/stable
sudo apt-get update
sudo apt install chromium-browser
yarn add puppeteer-core --dev
which chromium-browser

It was mentioned in different ways before by a few users, but somehow got lost in the thread.

This is what worked for me on WSL2 running an Ubuntu 20.04:

  1. Install Chrome. Here’s a guide.
  2. Run $ which google-chrome to check where it was installed. Probably /bin/google-chrome.
  3. Use const browser = await puppeteer.launch({ executablePath: "/bin/google-chrome" });

I followed these steps, but only needed to use const browser = await puppeteer.launch() without specifying the executablePath.

I just installed Chromium via sudo apt-get install chromium-browser (via https://github.com/GoogleChrome/puppeteer/issues/1700#issuecomment-369807464), and it started working with my basic test script, but still doesn’t work with my gulpfile for some reason, not sure what the difference is.

I’m using WSL2 on Windows build 19041 with Ubuntu 20.

I didn’t want to install an X server, and wanted native performance / display scaling / etc., so I tried very hard to get calling the native Windows Chrome executable via executablePath to work. It was a huge pain to try to get it to be happy with any userDataDir that was passed. It kept complaining that it couldn’t access or write to it, whether it was a path inside or outside WSL. Eventually, I did get it partially working to start up (with a variation of a 777’ed /tmp/.. or C:\tmp\.. by the end I think, but I didn’t chronicle that part), only to be faced with a cryptic [object Object] error with exit code 1, that I assume was thrown from some part of Puppeteer itself not enjoying an out-of-sync Chrome’s automation protocols. I don’t know if that was actually the issue or not, but for posterity, my native Chrome version as of this writing is 83.0.4103.97, and puppeteer version 3.3.0.

Finally I gave up. As @bruno222 suggested earlier in this issue, you can just install an X server and then let Puppeteer’s packaged Chromium have a display to make a visible window in. To do this, install https://sourceforge.net/projects/vcxsrv/ and follow the instructions to start it at https://nickymeuleman.netlify.app/blog/gui-on-wsl2-cypress (TL;DR - install normally, and on startup select ‘Multiple windows’, ‘Start no client’, and check on ‘Disable access control’). Then add this to your .bashrc / .zshrc, and source it or restart:

# Set up DISPLAY on the current container (to connect to VcXsrv)
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0

I also installed these packages inside of Ubuntu (pipe them into apt-get install), a step which might be required for you too if you didn’t install chromium-browser or any other GUI apps in it yet. I didn’t narrow down which are actually required, but certainly the X libs at least:

ca-certificates
fonts-liberation
gconf-service
libappindicator1
libasound2
libatk-bridge2.0-0
libatk1.0-0
libc6
libcairo2
libcups2
libdbus-1-3
libexpat1
libfontconfig1
libgbm1
libgcc1
libgconf-2-4
libgdk-pixbuf2.0-0
libglib2.0-0
libgtk-3-0
libnspr4
libnss3
libpango-1.0-0
libpangocairo-1.0-0
libstdc++6
libx11-6
libx11-xcb1
libxcb1
libxcomposite1
libxcursor1
libxdamage1
libxext6
libxfixes3
libxi6
libxrandr2
libxrender1
libxss1
libxtst6
lsb-release
wget
xdg-utils

If all went well, you should see Chromium pop up next time you start puppeteer in headless: false

Base on your list I have run this one-line command and it works now,

Thanks,

sudo apt install ca-certificates fonts-liberation gconf-service libappindicator1 libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils -y 
"--disable-gpu",
"--disable-setuid-sandbox",
"--no-sandbox",
"--single-process"

was the working combination for me with WSL1 on Windows 10 - 2004.

I’m using this to run pa11y

Thanks @Maxim-Mazurok! For me, just the --no-sandbox and --single-precess flags was needed. Good to be able to test hardware accelerated code too on the GPU 😃

'--no-sandbox',
'--single-process',

I’m using WSL2 on Windows build 19041 with Ubuntu 20.

I didn’t want to install an X server, and wanted native performance / display scaling / etc., so I tried very hard to get calling the native Windows Chrome executable via executablePath to work. It was a huge pain to try to get it to be happy with any userDataDir that was passed. It kept complaining that it couldn’t access or write to it, whether it was a path inside or outside WSL. Eventually, I did get it partially working to start up (with a variation of a 777’ed /tmp/.. or C:\tmp\.. by the end I think, but I didn’t chronicle that part), only to be faced with a cryptic [object Object] error with exit code 1, that I assume was thrown from some part of Puppeteer itself not enjoying an out-of-sync Chrome’s automation protocols. I don’t know if that was actually the issue or not, but for posterity, my native Chrome version as of this writing is 83.0.4103.97, and puppeteer version 3.3.0.

Finally I gave up. As @bruno222 suggested earlier in this issue, you can just install an X server and then let Puppeteer’s packaged Chromium have a display to make a visible window in. To do this, install https://sourceforge.net/projects/vcxsrv/ and follow the instructions to start it at https://nickymeuleman.netlify.app/blog/gui-on-wsl2-cypress (TL;DR - install normally, and on startup select ‘Multiple windows’, ‘Start no client’, and check on ‘Disable access control’). Then add this to your .bashrc / .zshrc, and source it or restart:

# Set up DISPLAY on the current container (to connect to VcXsrv)
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0

I also installed these packages inside of Ubuntu (pipe them into apt-get install), a step which might be required for you too if you didn’t install chromium-browser or any other GUI apps in it yet. I didn’t narrow down which are actually required, but certainly the X libs at least:

ca-certificates
fonts-liberation
gconf-service
libappindicator1
libasound2
libatk-bridge2.0-0
libatk1.0-0
libc6
libcairo2
libcups2
libdbus-1-3
libexpat1
libfontconfig1
libgbm1
libgcc1
libgconf-2-4
libgdk-pixbuf2.0-0
libglib2.0-0
libgtk-3-0
libnspr4
libnss3
libpango-1.0-0
libpangocairo-1.0-0
libstdc++6
libx11-6
libx11-xcb1
libxcb1
libxcomposite1
libxcursor1
libxdamage1
libxext6
libxfixes3
libxi6
libxrandr2
libxrender1
libxss1
libxtst6
lsb-release
wget
xdg-utils

If all went well, you should see Chromium pop up next time you start puppeteer in headless: false

Works great after upgrading to WSL2.

For me this was by far the best solution when comparing to the other two solutions:

  • linking to windows host chrome executable
  • --disable-setuid-sandbox and --no-sandbox (kinda unsafe if you don’t trust the places you will browse with puppeteer)

For upgrading to WSL2

  • I had to download the kernel update here https://docs.microsoft.com/en-us/windows/wsl/wsl2-kernel
  • Ensure you have hardware virtualization turned on, the command prompt isn’t that descriptive about it when you don’t.
  • Follow https://docs.microsoft.com/en-us/windows/wsl/install-win10#update-to-wsl-2
  • Also, for me doing the set-default-version wasn’t enough, to convert from wsl1 to wsl2 I had to run: wsl --list --verbose or wsl -l -v in short to see the name of my distro, and then wsl --set-version <distroname> 2. This starts the conversion process which took me 3-4 hours, even though it says “this may take a few minutes” so if you wanna be safe I would do it while you’re sleeping 😉. It will say something along the lines of “conversion has completed” so watch for that to confirm you upgraded. Otherwise, something is probably wrong.

I hope it helps someone out there and maybe the issue can be closed when WSL2 is more “stable”

i have this error after update to wsl2

I was unable to install chromium-browser through apt in the 20.20 version of Ubuntu WSL because it was “Unable to reach the snap store”.

Using chromium-browser installed via apt as the the executable the only flags I needed (after much trial and error) were --disable-gpu and --single-process.

For people coming to this issue in 2020, the “Timed out after 30000” message doesn’t actually tell you what the problem is, it just means that chrome isn’t launching successfully. To debug that, you can grab the command-line parameters puppeteer is using to launch chrome and then run that yourself manually. My issue was that I had accidentally defined the DISPLAY environment variable, in WSL2 and chrome was hanging trying to connect to X.

It would be great if puppeteer could scoop up the chrome output on timeout and display it to aide with debugging.

Just adding my experience as it seems to differ from others here. On Windows 10.0.18362, Ubuntu 18.04, puppeteer 2.0.0.

The error I get after a wait is:

(node:2369) UnhandledPromiseRejectionWarning: TimeoutError: Timed out after 30000 ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r706915
    at Timeout.onTimeout (/mnt/c/Users/tarek/Code/picogl.js/node_modules/puppeteer/lib/Launcher.js:359:14)
    at ontimeout (timers.js:424:11)
    at tryOnTimeout (timers.js:288:5)
    at listOnTimeout (timers.js:251:5)
    at Timer.processTimers (timers.js:211:10)

I’m using the locally-installed chromium that comes with puppeteer, so the the version mismatch doesn’t make much sense.

I couldn’t get native puppeteer to work (on WSL1 and Ubuntu), but I was able to get my code to work with Windows Chrome using the following:

  1. I added the folder containing chrome.exe to my PATH in WSL. I am using zsh for my shell, but I think it would be the same for bash. I put this in my ~/.zshrc:

export PATH=/mnt/c/Program\ Files\ \(x86\)/Google/Chrome/Application:$PATH

. 2. In my code, I launched headless chrome with the following:

const browser = await puppeteer.launch({ executablePath: 'chrome.exe', headless: false });

So I tried running it on wsl 2 and it just wouldn’t work. Then I ran sudo apt install chromium-browser to install chromium. After that was done, in my project directory, I ran npm i puppeteer-core. After that, I included puppeteer-core in my app.js and I ran which chromium-browser to find the location of the browser itself. Once I had that, I added the path to it in the puppeteer.launch function as the executablePath argument. So my code now looks like this:

const puppeteer = require("puppeteer-core");

(async () => {
  const browser = await puppeteer.launch({
    executablePath: "/usr/bin/chromium-browser"
  });
  const page = await browser.newPage();
  await page.goto("https://www.example.com");
  await page.screenshot({ path: "example.png" });

  await browser.close();
})();

And it works flawlessly! I don’t know if it works on wsl 1, I haven’t tried. Someone try it and let me know. I hope this helps you guys!

cant wait to get wsl2 …

Testing on my original machine, getting the exact same error. This worked once, no clue why it wouldn’t be working now. Scanning through the code where the error is occurring, it seems that the problem is something to do with the forceKillChrome() function, possibly related to how it checks what plat form it’s being run on.

This is the line that’s causing the error.

Things I’ve found while debugging:

  • console.log(process.platform) returns linux
  • console.log(typeof process.kill) returns function
  • console.log(chromeProcess.pid) returns the process number

All that seems fine to me, I can’t tell what would be causing the issue.

Some Googling, it sounds like it may be an issue with the wrong pid being returned on WSL. – see https://github.com/Tyriar/node-pty/issues/45