Search code examples
javascriptasync-awaitplaywright

Playwright Library throws `Target closed` error only if `context` or `browser` is closed


The following Node.js script attempts to use the Playwright Library for browser automation (not Playwright Test) to scrape some data from a local website and log the output to the terminal. The script throws the error: locator.innerText: Target closed, which points to the rows definition.

The script succeeds when the line await context.close(); is commented out, but then the terminal session hangs which is undesirable.

Why is the failure thrown and how to fix it?

import { firefox } from 'playwright';

(async () => {
  const browser = await firefox.launch({ headless: true });
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto(
    'https://example.com'
  );

  const rows = await page
    .locator('.row')
    .filter({ hasText: 'some text that some rows have' })
    .all();

  rows.forEach(async (row) => {
    const time = await row.locator('.time').innerText();
    const heading = await row.getByText('some common heading').innerText();

    console.log(`${time} ${heading}`);
  });

  // When context is closed via the following uncommented line then the script fails with 
  // the error `locator.innerText: Target closed`.
  // When the following uncommented line is commented out then the script logs the 
  // expected output, but the terminal session hangs because the browser is still open.
  await context.close();
})();

Solution

  • The error happens because the forEach loop does not actually wait for completion of asynchronous operations inside it and context.close() is called faster.

    You can't use async/await with forEach. Instead, try changing it to for...of:

    for (const row of rows) {
      const time = await row.locator('.time').innerText();
      const heading = await row.getByText('some common heading').innerText();
    
      console.log(`${time} ${heading}`);
    }
    
    

    Also, if you want to close the terminal session, you should call browser.close() instead of context.close().