Playwright Test Failing in CI but Passing Locally: Complete Troubleshooting Guide

Introduction: Why Do Playwright Tests Pass Locally but Fail in CI?

One of the most frustrating automation problems is:

Local → PASS

CI/CD → FAIL

The test code may not have changed, yet a Playwright test fails in GitHub Actions, Jenkins, Azure DevOps, or another CI server.

The reason is simple: local and CI are different execution environments.

A developer machine may have:

  • A different operating system
  • More CPU and memory
  • Different browser versions
  • Cached Playwright browsers
  • Local environment variables
  • Existing authentication
  • Faster network access
  • Different test data

CI may have none of these.

This playwright test failing in ci but passing locally guide explains how to identify the actual cause instead of simply increasing timeouts or retries.


Common Reasons for Playwright CI Failures

The most common causes are:

CauseLocalCI
Browser dependenciesAlready installedMissing
Environment variables.env availableMissing secret
CPU/memoryUsually higherLimited
Browser modeOften headed during debuggingUsually headless
OSWindows/macOSUsually Linux
AuthenticationExisting/local stateFresh environment
NetworkFast/localRestricted/slower
Test dataExisting dataDifferent/empty
Parallel workersFewerMore
Application URLLocal configCI config

The first troubleshooting step is therefore to identify what is different, not to change the test randomly.


Local Environment vs CI Environment Differences

Start by printing safe environment information.

import { test } from ‘@playwright/test’;

test(‘environment diagnostics’, async ({ page }) => {

 console.log(‘URL:’, process.env.BASE_URL);

 console.log(‘CI:’, process.env.CI);

 console.log(‘Platform:’, process.platform);

 await page.goto(process.env.BASE_URL!);

 console.log(‘Actual URL:’, page.url());

});

Never print passwords, API keys, tokens, cookies, or authentication state into CI logs.

A useful comparison is:

Local:

Node version

Playwright version

Browser version

OS

BASE_URL

Test data

Authentication

CI:

Node version

Playwright version

Browser version

OS

BASE_URL

Test data

Authentication

Differences in any of these can explain a Playwright CI Failure.


Browser Installation and Dependency Issues

A very common CI problem is that npm dependencies are installed but Playwright browsers are not.

This:

npm ci

does not necessarily mean your CI environment has the required browser binaries available.

Install them explicitly:

npx playwright install –with-deps

For Chromium only:

npx playwright install chromium –with-deps

A typical Linux CI sequence is:

npm ci

npx playwright install –with-deps

npx playwright test

This is particularly important when the local machine already has Playwright browsers cached.


Headless Browser and Operating-System Differences

CI commonly runs browsers headlessly on Linux.

A test may behave differently because:

  • Font rendering differs.
  • Viewport differs.
  • Linux dependencies differ.
  • Animations behave differently.
  • Browser versions differ.
  • OS-specific behavior changes.

Configure the viewport explicitly when it matters:

import { defineConfig } from ‘@playwright/test’;

export default defineConfig({

 use: {

   viewport: {

     width: 1280,

     height: 720

   },

   headless: true

 }

});

Don’t attempt to reproduce every local machine detail. Instead, make the test environment deterministic.


Environment Variables, URLs, and Test Credentials

This is another major cause of playwright test failing in ci but passing locally.

Local:

BASE_URL=https://staging.example.com

TEST_USERNAME=qa-user

TEST_PASSWORD=******

CI might have:

BASE_URL=https://wrong-environment.example.com

or no credentials at all.

Use environment variables:

const baseURL = process.env.BASE_URL;

if (!baseURL) {

 throw new Error(‘BASE_URL is not configured’);

}

Then:

await page.goto(baseURL);

For credentials:

await page.getByLabel(‘Email’)

 .fill(process.env.TEST_USERNAME!);

await page.getByLabel(‘Password’)

 .fill(process.env.TEST_PASSWORD!);

Store credentials in CI secret management rather than committing them to Git.


Timeout and Synchronization Problems

CI machines can be slower than developer machines.

A test like this is fragile:

await page.getByRole(‘button’, {

 name: ‘Generate Report

}).click();

await page.waitForTimeout(2000);

await expect(

 page.getByText(‘Report ready’)

).toBeVisible();

The 2-second wait might work locally but fail on a busy CI runner.

Use application-state synchronization:

await page.getByRole(‘button’, {

 name: ‘Generate Report’

}).click();

await expect(

 page.getByText(‘Report ready’)

).toBeVisible({

 timeout: 20_000

});

The important distinction is that the assertion waits for a meaningful condition.

Don’t simply turn every timeout into:

timeout: 120000

That can hide a real synchronization problem.


Flaky Tests and Parallel Execution Issues

CI often exposes Playwright Test Flakiness that isn’t obvious locally.

Suppose two tests use the same account:

Worker 1 → Add product to cart

Worker 2 → Remove product from cart

The result becomes unpredictable.

Run the suite with one worker as a diagnostic:

npx playwright test –workers=1

If failures disappear, investigate shared state.

Possible conflicts include:

  • Same user
  • Same shopping cart
  • Same database record
  • Same file
  • Same order
  • Same test account

The solution is test isolation, not permanently disabling parallel execution.


Test Data, Authentication, and Shared-State Problems

A local machine might already have authentication cookies or data.

CI starts from a clean environment.

For authenticated tests, use controlled authentication setup and storageState where appropriate.

Example:

await page.context().storageState({

 path: ‘playwright/.auth/user.json’

});

Then configure:

use: {

 storageState: ‘playwright/.auth/user.json’

}

However, authentication files can contain sensitive state and should not be committed to source control.

For CI, generate authentication state as part of the pipeline or setup process using secure credentials.


Debugging Playwright Failures in CI

When a test fails only in CI, you need evidence.

Configure:

import { defineConfig } from ‘@playwright/test’;

export default defineConfig({

 use: {

   screenshot: ‘only-on-failure’,

   video: ‘retain-on-failure’,

   trace: ‘retain-on-failure’

 },

 reporter: [

   [‘html’]

 ]

});

Now a failed test can provide:

Open the report locally after downloading the CI artifact:

npx playwright show-report

Trace Viewer can reveal:

Last successful action

       ↓

Locator resolution

       ↓

Page screenshot

       ↓

Network activity

       ↓

Failure

This is one of the most effective techniques for Playwright CI debugging.


Playwright GitHub Actions Troubleshooting Example

A practical GitHub Actions workflow:

name: Playwright Tests

on:

 push:

   branches: [main]

 pull_request:

jobs:

 test:

   runs-on: ubuntu-latest

   steps:

     – name: Checkout

       uses: actions/checkout@v6

     – name: Setup Node

       uses: actions/setup-node@v6

       with:

         node-version: lts/*

     – name: Install dependencies

       run: npm ci

     – name: Install Playwright browsers

       run: npx playwright install –with-deps

     – name: Run Playwright tests

       run: npx playwright test

     – name: Upload Playwright report

       if: ${{ !cancelled() }}

       uses: actions/upload-artifact@v5

       with:

         name: playwright-report

         path: playwright-report/

Playwright’s CI documentation recommends installing dependencies and browsers before running the tests, with reports and other artifacts available for failures.

For secrets, configure GitHub repository or environment secrets and access them through environment variables:

– name: Run tests

 env:

   BASE_URL: ${{ secrets.BASE_URL }}

   TEST_USERNAME: ${{ secrets.TEST_USERNAME }}

   TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

 run: npx playwright test

Never put real credentials directly into YAML.


Configuring Retries and Workers

Retries can help identify environmental failures:

import { defineConfig } from ‘@playwright/test’;

export default defineConfig({

 retries: process.env.CI ? 2 : 0,

 workers: process.env.CI ? 2 : undefined

});

But retries should not become the primary solution.

If this happens:

Attempt 1 → FAIL

Attempt 2 → PASS

investigate the reason.

Similarly, reducing workers may make CI stable temporarily, but shared test data should ultimately be fixed.


Docker-Based CI Troubleshooting

Docker can reduce environment differences by standardizing:

  • Node
  • Browser
  • Linux dependencies
  • Playwright version

Example:

FROM mcr.microsoft.com/playwright:v1.62.0-noble

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

CMD [“npx”, “playwright”, “test”]

Keep the Docker image and Playwright package versions aligned.

Then:

docker build -t playwright-tests .

docker run –rm playwright-tests

Docker is particularly useful when a team wants Playwright Docker execution to resemble CI closely.


Real-World CI Failure Scenarios

Scenario 1: Browser works locally but not CI

Problem: Local PASS, CI says browser executable is missing.

Root cause: Browser binaries were not installed.

Fix:

npx playwright install –with-deps

Best practice: Make browser installation an explicit pipeline step.


Scenario 2: Locator timeout in CI

Problem: Button exists locally but times out in CI.

Root cause: Slower rendering or API response.

Incorrect approach:

await page.waitForTimeout(10000);

Fix:

await expect(

 page.getByRole(‘button’, {

   name: ‘Checkout’

 })

).toBeVisible({

 timeout: 15000

});

Best practice: Use auto-waiting and meaningful assertions.


Scenario 3: Authentication failure

Problem: Dashboard test fails because the dashboard isn’t found.

Root cause: CI credentials are missing or invalid.

Fix: Verify the CI secret and assert authentication:

await expect(page).toHaveURL(/dashboard/);

Best practice: Separate authentication setup from business workflow tests.


Scenario 4: Parallel test failure

Problem: Tests pass with one worker but fail with four.

Root cause: Shared test data.

Fix: Generate independent users/orders or isolate database records.

Best practice: Design tests to be worker-safe.


Playwright CI/CD Best Practices

Use this checklist when troubleshooting playwright test failing in ci but passing locally:

Environment

  • Pin Node versions.
  • Lock dependencies.
  • Standardize browsers.
  • Use Docker when appropriate.
  • Explicitly install browser dependencies.

Test design

  • Use reliable locators.
  • Avoid waitForTimeout().
  • Use web-first assertions.
  • Make tests independent.
  • Generate isolated test data.

Authentication

  • Use dedicated CI test accounts.
  • Store credentials as secrets.
  • Generate authentication state securely.
  • Never commit tokens or cookies.

CI configuration

  • Install browsers.
  • Configure required environment variables.
  • Collect traces.
  • Upload screenshots and reports.
  • Use retries only as a safety mechanism.

Parallel execution

  • Avoid shared users.
  • Avoid shared files.
  • Avoid shared mutable records.
  • Verify tests with multiple workers.

Playwright Interview Questions with Answers

Why does Playwright pass locally but fail in CI?

Because CI may use different operating systems, browsers, environment variables, credentials, network conditions, resources, or test data.

How do you debug a Playwright CI failure?

Collect screenshots, videos, traces, HTML reports, logs, and environment information, then inspect the failing action.

Should you increase timeout when CI is slow?

Only when the application operation is legitimately slower. First investigate synchronization and environment issues.

How do you install browsers in GitHub Actions?

npx playwright install –with-deps

How do you diagnose parallel failures?

Run:

npx playwright test –workers=1

If the problem disappears, investigate shared state and test isolation.

Why use Docker for Playwright?

Docker helps standardize the browser, OS dependencies, Node environment, and Playwright version.


FAQs: Playwright Test Failing in CI but Passing Locally

Why does Playwright pass locally but fail in CI?

The local and CI environments may have different browsers, operating systems, environment variables, credentials, network conditions, resources, or test data.

How do I fix Playwright CI failures?

Start by comparing environments. Install browsers and dependencies, verify secrets and URLs, collect traces, check synchronization, and investigate test isolation.

Why does Playwright timeout only in CI?

CI machines can be slower, APIs may respond differently, or the test may depend on timing. Use web-first assertions and investigate the CI environment.

Why does Playwright authentication work locally but fail in CI?

Local authentication may rely on existing cookies or environment variables. CI starts clean and requires explicit credentials and authentication setup.

Should I use more retries in CI?

Retries can reduce the impact of transient infrastructure problems, but repeated retry success is a signal that the test may be flaky.

Does Docker fix all Playwright CI problems?

No. Docker standardizes the execution environment but cannot fix incorrect locators, bad test data, authentication errors, or application defects.

Leave a Comment

Your email address will not be published. Required fields are marked *