Skip to main content

Troubleshooting Playwright Lighthouse Integration

Fix common issues when running Lighthouse with Playwright: port conflicts, authentication problems, flaky scores, and Chrome version mismatches.
Harlan WiltonHarlan Wilton Published Updated

Common issues and solutions when integrating Lighthouse with Playwright.

Port Already in Use

Error: Error: listen EADDRINUSE: address already in use :::9222

The debugging port is already occupied, usually from a previous run that didn't close properly.

Solution: Identify the process before stopping it, or assign a different port:

# Show the process listening on port 9222
lsof -nP -iTCP:9222 -sTCP:LISTEN
const PORT = 9223

For CI environments, use proper cleanup:

async function audit() {
  let browser
  try {
    browser = await chromium.launch({
      args: [`--remote-debugging-port=${PORT}`],
    })
    // ... audit logic
  }
  finally {
    if (browser)
      await browser.close()
  }
}

Authentication State Lost

Symptom: Lighthouse audits a login page instead of the authenticated page.

Cause: Lighthouse opens a new page context, losing cookies/session.

Solution: Use disableStorageReset: true:

const result = await lighthouse(url, {
  port: PORT,
  disableStorageReset: true, // Prevents Lighthouse from clearing storage
})

See Authentication Guide for complete examples.

Flaky or Inconsistent Scores

Symptom: Performance scores vary significantly between runs (±10-20 points).

Causes:

  • Network variability
  • CPU load on the machine
  • Shared CI runners
  • Third-party script timing

Solutions:

  1. Run multiple audits and use median:
const runs = 3
const scores = []
for (let i = 0; i < runs; i++) {
  const result = await lighthouse(url, { port: PORT })
  scores.push(result.lhr.categories.performance.score)
}
scores.sort((a, b) => a - b)
const median = scores[Math.floor(scores.length / 2)]
  1. Use the provided network without Lighthouse simulation for local diagnostics:
const result = await lighthouse(url, {
  port: PORT,
  throttlingMethod: 'provided',
})

Use simulated throttling again for performance budgets. Unthrottled scores describe your test machine and should not be compared with PageSpeed Insights.

  1. Use dedicated CI runners instead of shared ones.

Chrome/Chromium Version Mismatch

Error: Protocol error or Target closed or Cannot find context with specified id

Cause: The installed Playwright package, browser binary, and Lighthouse version came from different lockfile states or caches.

Solution: Check the installed versions, reinstall Playwright's matching Chromium, and let Playwright use its bundled executable:

npm ls @playwright/test lighthouse
npx playwright install chromium

Playwright warns that custom executables may not work with its APIs. If you deliberately test branded Chrome, select a supported channel and keep that choice explicit:

const browser = await chromium.launch({
  channel: 'chrome',
  args: [`--remote-debugging-port=${PORT}`],
})

Timeout Errors

Error: Lighthouse timeout or Navigation timeout exceeded

Causes:

  • Page takes too long to load
  • waitUntil: 'networkidle' waiting for never-ending requests
  • Large pages with many resources

Solutions:

  1. Increase Lighthouse timeout:
const result = await lighthouse(url, {
  port: PORT,
  maxWaitForLoad: 60000, // 60 seconds (default is 45s)
})
  1. Use different wait strategy:
// Instead of networkidle, wait for specific element
await page.goto(url)
await page.waitForSelector('#main-content')
  1. Check for infinite polling requests (analytics, websockets).

Sandbox Errors

Error: No usable sandbox! or Running as root without --no-sandbox is not supported

Cause: Chrome sandboxing issues, common in Docker/CI.

Solution: Disable sandbox (only in trusted CI environments):

const browser = await chromium.launch({
  args: [
    `--remote-debugging-port=${PORT}`,
    '--no-sandbox',
    '--disable-setuid-sandbox',
  ],
})
Only disable sandbox in controlled CI environments, never in production or when processing untrusted URLs.

Multiple Concurrent Audits Fail

Error: Audits interfere with each other or return incorrect results.

Cause: Multiple Lighthouse instances trying to use the same debugging port.

Solution: Use different ports for parallel audits:

async function auditWithDynamicPort(url) {
  const port = 9222 + Math.floor(Math.random() * 1000)
  const browser = await chromium.launch({
    args: [`--remote-debugging-port=${port}`],
  })
  // ... use `port` variable
}

Or run audits sequentially with a single worker:

# Playwright Test
npx playwright test --workers=1

Lighthouse Reports Show Wrong Page

Symptom: Report shows homepage or login page instead of target URL.

Causes:

  • Redirects before audit runs
  • Authentication issues
  • URL mismatch between Playwright navigation and Lighthouse call

Solution: Verify URL after navigation:

await page.goto(targetUrl, { waitUntil: 'networkidle' })

// Check actual URL
const actualUrl = page.url()
if (actualUrl !== targetUrl) {
  console.warn(`Redirected from ${targetUrl} to ${actualUrl}`)
}

// Use actual URL for Lighthouse
const result = await lighthouse(actualUrl, { port: PORT })

Empty or Incomplete Reports

Symptom: Lighthouse report missing metrics or categories.

Causes:

  • Page not fully loaded
  • JavaScript errors on page
  • Missing viewport settings

Solution: Check page readiness:

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

// Wait for critical content
await page.waitForSelector('[data-testid="main-content"]')

// Check for console errors
page.on('console', (msg) => {
  if (msg.type() === 'error')
    console.warn('Page error:', msg.text())
})

CI-Specific Issues

GitHub Actions: Chrome Not Found

- name: Install Chrome
  run: npx playwright install chromium --with-deps

The --with-deps flag installs system dependencies required by Chromium.

Docker: Missing Dependencies

Add to Dockerfile:

RUN apt-get update && apt-get install -y \
    libnss3 \
    libatk1.0-0 \
    libatk-bridge2.0-0 \
    libcups2 \
    libdrm2 \
    libxkbcommon0 \
    libxcomposite1 \
    libxdamage1 \
    libxrandr2 \
    libgbm1 \
    libasound2

Or use Playwright's Docker image. Pin the image tag to the same Playwright version as @playwright/test; mismatched versions can prevent the client from locating browser executables.

Crash / Target Closed in Docker: If Chrome crashes with "Target closed" or "Page crashed", it's often due to small shared memory /dev/shm. Add this flag:

const browser = await chromium.launch({
  args: [
    `--remote-debugging-port=${PORT}`,
    '--disable-dev-shm-usage', // Critical for Docker
  ],
})

Visual Debugging

Use Playwright's Inspector to pause execution right before the Lighthouse audit to verify the page state.

# Run with Inspector
PWDEBUG=1 npx playwright test

When paused:

  1. Check that the page has logged you in (if expected).
  2. Verify the browser has fully loaded the DOM.
  3. Check for any overlay/modals that might block Lighthouse.

Still Stuck?