---
title: "Lighthouse CI Troubleshooting Guide"
description: "Fix common Lighthouse CI issues: Chrome launch failures, score variance, shallow clone errors, upload problems, and more."
canonical_url: "https://unlighthouse.dev/learn-lighthouse/lighthouse-ci/troubleshooting"
last_updated: "2026-07-05T10:27:34.363Z"
---

Common issues when running Lighthouse CI and how to fix them.

## Recommended Chrome Flags for CI

Most CI Chrome issues can be solved with this flag combination:

```js
settings: {
  chromeFlags: [
    '--no-sandbox', // Required in containers
    '--disable-setuid-sandbox', // Backup for setuid sandbox issues
    '--disable-dev-shm-usage', // Avoid /dev/shm size issues
    '--disable-gpu', // GPU not available in most CI
  ].join(' ')
}
```

## Chrome Launch Failures

### Chrome Not Found

**Problem:** `Error: Unable to find Chrome` or `Chrome could not be found on the system`

**Solution:** Install Chrome on your CI runner:

```bash
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y wget gnupg
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
sudo apt-get update
sudo apt-get install -y google-chrome-stable

# Or use puppeteer's Chrome
npx @puppeteer/browsers install chrome@stable
```

For GitHub Actions, use the pre-installed Chrome:

```yaml
- name: Run Lighthouse CI
  run: npx lhci autorun
  env:
    CHROME_PATH: /usr/bin/google-chrome
```

### Protocol Error: X.Y wasn't found

**Problem:** `Protocol error (X.Y): Z wasn't found` or Chrome DevTools Protocol errors

**Solution:** Update to latest Lighthouse CI version (Chrome DevTools Protocol changes frequently):

```bash
npm install -D @lhci/cli@latest
```

If using an older Node version, you may need to upgrade [Node.js](https://nodejs.org) to match the Chrome version installed on your system.

### No usable sandbox

**Problem:** `Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted` or `No usable sandbox!`

**Why this happens:** [Chrome's sandbox](https://chromium.googlesource.com/chromium/src/+/0e94f26e8/docs/linux_sandboxing.md) uses Linux user namespaces for isolation. Docker's default seccomp profile blocks these syscalls because the container already provides isolation - creating a conflict. On [Ubuntu 24.04+, AppArmor restrictions](https://ubuntu.com/blog/whats-new-in-security-for-ubuntu-24-04-lts) add another layer requiring explicit profiles.

**Solution:** Add `--no-sandbox` flag in CI environments without proper sandboxing:

```js
// lighthouserc.js
export default {
  ci: {
    collect: {
      chromePath: process.env.CHROME_PATH,
      settings: {
        chromeFlags: '--no-sandbox --disable-gpu'
      }
    }
  }
}
```

**Warning:** Only use `--no-sandbox` in trusted CI environments running your own code. If auditing untrusted sites, [malicious pages could escape](https://googlechrome.github.io/lighthouse-ci/docs/recipes/docker-client/). For maximum security, use [Jess Frazelle's Chrome seccomp profile](https://github.com/jessfraz/dotfiles/blob/main/etc/docker/seccomp/chrome.json).

### Browser Crashes in Docker (TARGET_CRASHED)

**Problem:** `Browser tab has unexpectedly crashed` or `TARGET_CRASHED` errors in [Docker](https://docker.com)

**Why this happens:** [Docker's default 64MB /dev/shm](https://bugs.chromium.org/p/chromium/issues/detail?id=715363) is too small for Chrome's shared memory requirements when rendering large pages.

**Solution:** Chrome needs adequate shared memory. Add `--disable-dev-shm-usage` flag (writes to disk instead, slower):

```js
// lighthouserc.js
const config2 = {
  ci: {
    collect: {
      settings: {
        chromeFlags: '--no-sandbox --disable-dev-shm-usage --disable-gpu'
      }
    }
  }
}
```

Or mount a larger `/dev/shm` volume when running Docker (better performance):

```bash
docker run --shm-size=2g your-image
```

<note>

Docker Desktop with WSL2 has an [additional issue](https://github.com/SeleniumHQ/docker-selenium/issues/1309) where /dev/shm may lack execution permissions.

</note>

## Bot Detection and 403 Errors

**Problem:** `403 Forbidden` errors when auditing production sites with bot protection

**Why this happens:** Lighthouse's automated Chrome requests score 1-29 on [Cloudflare's bot score](https://developers.cloudflare.com/waf/custom-rules/use-cases/challenge-bad-bots/), triggering blocks. [Bot Fight Mode specifically blocks Lighthouse](https://community.cloudflare.com/t/bot-fight-mode-challenging-chrome-lighthouse/368829) and causes TBT to spike.

**Solution:** Sites with [Cloudflare](https://cloudflare.com), WAFs, or bot detection may block Lighthouse's requests.

Options:

1. **Disable Bot Fight Mode** (Cloudflare) - it's [meant for active attacks only](https://community.cloudflare.com/t/bot-fight-mode-challenging-chrome-lighthouse/368829)
2. **Test against origin**: bypass CDN for CI testing
3. **Whitelist CI runner IPs**: but be aware IPs change on shared runners
4. **Test staging environments** without bot protection enabled

<warning>

[Skip rules don't work for Super Bot Fight Mode](https://community.cloudflare.com/t/turn-off-bot-detection-for-specific-url/565739). You must disable SBFM or test a different endpoint.

</warning>

This cannot be fixed in Lighthouse CI configuration - it requires changes on the target site's security settings.

## Shallow Clone Issues

### Ancestor hash not found

**Problem:** `Could not find hash XXXXXX` or `fatal: bad object` when comparing branches

**Solution:** Increase fetch depth in GitHub Actions:

```yaml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0 # Full history (safest)
    # Or use minimum depth for faster checkout:
    # fetch-depth: 20
```

For GitLab CI:

```yaml
variables:
  GIT_DEPTH: 0
```

Lighthouse CI needs git history to compare against base branch commits. See [GitHub Actions setup](/learn-lighthouse/lighthouse-ci/github-actions) for complete configuration.

## Score Variance

### Inconsistent scores across runs

**Problem:** Performance scores vary by 5-15 points between identical runs

**Why this happens:** [Google identifies 7 primary sources of variability](https://developers.google.com/web/tools/lighthouse/variability):

<table>
<thead>
  <tr>
    <th>
      Source
    </th>
    
    <th>
      Impact
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Page nondeterminism (A/B tests, ads)
    </td>
    
    <td>
      High
    </td>
  </tr>
  
  <tr>
    <td>
      Local network variability
    </td>
    
    <td>
      High
    </td>
  </tr>
  
  <tr>
    <td>
      Client hardware variability
    </td>
    
    <td>
      High
    </td>
  </tr>
  
  <tr>
    <td>
      Client resource contention
    </td>
    
    <td>
      High
    </td>
  </tr>
  
  <tr>
    <td>
      Tier-1 network variability
    </td>
    
    <td>
      Medium
    </td>
  </tr>
  
  <tr>
    <td>
      Browser nondeterminism
    </td>
    
    <td>
      Medium
    </td>
  </tr>
  
  <tr>
    <td>
      Web server variability
    </td>
    
    <td>
      Low
    </td>
  </tr>
</tbody>
</table>

**Solution:** Increase `numberOfRuns` and use consistent hardware:

```js
// lighthouserc.js
const config3 = {
  ci: {
    collect: {
      numberOfRuns: 5, // Run 5 times, take median
      settings: {
        onlyCategories: ['performance'],
        // Disable CPU throttling variance
        throttling: {
          cpuSlowdownMultiplier: 1
        }
      }
    },
    assert: {
      assertions: {
        // Use ranges for flaky metrics
        'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
        'interactive': ['warn', { maxNumericValue: 5000 }]
      }
    }
  }
}
```

**Best practices to reduce variance:**

- Use dedicated CI runners (not shared)
- Minimum [2 cores and 4GB RAM](https://googlechrome.github.io/lighthouse-ci/docs/troubleshooting.html) - avoid burstable instances
- Use [simulated throttling](https://github.com/GoogleChrome/lighthouse/blob/master/docs/throttling.md) (default) for lowest variance
- [5 runs is 2x more stable](https://developers.google.com/web/tools/lighthouse/variability) than 1 run; 3 runs reduces variance by 37%
- Avoid testing on CI for precise performance measurements (consider real user monitoring instead)

Performance scores naturally vary ±5 points even on identical hardware. Focus on trends over time rather than individual runs.

<note type="info">

**December 2024 change**: [PageSpeed Insights changed CPU throttling from 4x to 1.2x](https://developers.google.com/speed/docs/insights/release_notes) to better match production environments. If your PSI scores suddenly improved, this is why.

</note>

Understanding the metrics you're testing helps interpret variance. See guides for [LCP](/learn-lighthouse/lcp), [CLS](/learn-lighthouse/cls), and [INP](/learn-lighthouse/inp).

## Upload Issues

### GitHub App not working on fork PRs

**Problem:** Status checks don't appear on pull requests from forks

**Solution:** This is a security limitation. Forks can't access repository secrets or GitHub Apps. Options:

1. **Require PR authors to create issues instead of PRs** (community contributions workflow)
2. **Use pull_request_target with caution:**

```yaml
on:
  pull_request_target: # Runs in base repo context

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      # DANGER: Only checkout base branch, never fork code
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.base_ref }}

      - name: Run Lighthouse CI
        run: npx lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
```

**Warning:** `pull_request_target` runs with write access. Never checkout or execute fork code.

### Token security for public repos

**Problem:** Don't want to expose server token in public repo

**Solution:** Use GitHub Secrets and the Lighthouse CI GitHub App:

```yaml
env:
  LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
```

Never hardcode tokens in `lighthouserc.js`. For public repos, the GitHub App is safer than `upload.token` because it scopes permissions to status checks only.

## URL Normalization

### Dynamic URLs with ports or UUIDs

**Problem:** Each CI run creates different URLs (localhost:34567, preview-abc123.app.com) that can't be compared historically

**Solution:** Use `--url-replacement-patterns` to normalize URLs:

```bash
# Normalize port numbers
lhci collect --url-replacement-patterns='http://localhost:[0-9]+/=//'

# Normalize preview UUIDs
lhci collect --url-replacement-patterns='https://preview-[a-f0-9]+.app.com/=//'
```

Or in configuration:

```js
// lighthouserc.js
const config4 = {
  ci: {
    collect: {
      url: ['http://localhost:8080'],
      urlReplacementPatterns: [
        's/localhost:[0-9]+/localhost:PORT/g',
        's/preview-[a-f0-9]+/preview-UUID/g'
      ]
    }
  }
}
```

This makes sure historical data aggregates properly across different deployments.

## Lost Tokens

### Recovering Lost GitHub App Token

**Problem:** Lost GitHub App token after initial setup

**Solution:** Uninstall and reinstall the [Lighthouse CI GitHub App](https://github.com/apps/lighthouse-ci) on your repository to get a new token.

### Recovering Lost Build Token (LHCI Server)

**Problem:** Lost build token for uploading to your LHCI server

**Solution:** Reset the build token via wizard:

```bash
# Connect to your LHCI server and run:
lhci wizard --wizard=reset-build-token
```

Or create a new project via API if you have admin token:

```bash
curl -X POST https://your-lhci-server.com/v1/projects \
  -H "Authorization: Bearer $LHCI_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-project", "externalUrl": "https://github.com/user/repo"}'
```

### Admin vs Build tokens

- **Admin token:** Full server access, manage projects. Keep secret, never commit.
- **Build token:** Upload-only for specific project. Safe in CI secrets.
- **GitHub App token:** Status check posting only. Safest for public repos.

## Configuration Issues

### lighthouserc.js not found

**Problem:** `Could not find configuration file`

**Solution**: Check that file is in project root and properly named:

```bash
# Must be one of these names
lighthouserc.js
lighthouserc.json
.lighthouserc.js
.lighthouserc.json

# Or specify explicitly
lhci autorun --config=./path/to/lighthouserc.js
```

### Invalid configuration format

**Problem:** `Configuration parsing failed` or `Invalid config`

**Solution:** Validate syntax. Configuration must export an object with `ci` key:

```js
// ✅ Correct
const config5 = {
  ci: {
    collect: { /* ... */ }
  }
}

// ❌ Wrong - missing 'ci' wrapper
const config6 = {
  collect: { /* ... */ }
}
```

See [configuration reference](/learn-lighthouse/lighthouse-ci/configuration) for complete schema.

## Memory and Timeout Issues

### Out of memory on large sites

**Problem:** `JavaScript heap out of memory` or CI runner killed

**Solution:** Increase Node memory and reduce concurrent audits:

```bash
# Increase Node memory
NODE_OPTIONS=--max-old-space-size=4096 lhci autorun
```

```js
// lighthouserc.js - reduce load
const config7 = {
  ci: {
    collect: {
      numberOfRuns: 3, // Reduce from 5
      url: [
        // Audit fewer pages
        'http://localhost:8080',
        'http://localhost:8080/about'
      ]
    }
  }
}
```

For large sites (50+ pages), consider:

- Split audits across multiple jobs
- Use more powerful CI runners
- Audit subset of critical pages only

### CI timeout on slow runners

**Problem:** CI job exceeds time limit (30min default on GitHub Actions)

**Solution:** Optimize collection or increase timeout:

```yaml
jobs:
  lighthouse:
    timeout-minutes: 60 # Default is 360 (6h), reduce for faster failure detection
```

```js
// lighthouserc.js - faster collection
const config8 = {
  ci: {
    collect: {
      numberOfRuns: 3, // Reduce runs
      settings: {
        onlyCategories: ['performance'], // Single category
        skipAudits: ['screenshot-thumbnails', 'final-screenshot'] // Skip slow audits
      }
    }
  }
}
```

## startServerCommand Issues

### Server not starting

**Problem:** `startServerCommand` runs but server never becomes available

**Solution:** Add explicit readiness check:

```js
// lighthouserc.js
const config9 = {
  ci: {
    collect: {
      startServerCommand: 'npm run serve',
      startServerReadyPattern: 'Local:.*http://localhost:8080', // Wait for this log
      startServerReadyTimeout: 30000, // 30s timeout
      url: ['http://localhost:8080']
    }
  }
}
```

Common patterns:

- [Vite](https://vite.dev): `Local:.*http://localhost`
- [Next.js](https://nextjs.org): `ready on`
- Express: `listening on`

### Wrong port

**Problem:** Server starts on random port, hardcoded URL fails

**Solution:** Force specific port in start command:

```bash
# package.json
{
  "scripts": {
    "serve": "vite preview --port 8080 --strictPort"
  }
}
```

Or set port via environment variable:

```js
// lighthouserc.js
const config10 = {
  ci: {
    collect: {
      startServerCommand: 'PORT=8080 npm run serve',
      startServerReadyPattern: 'http://localhost',
      url: ['http://localhost:8080']
    }
  }
}
```

## Assertion Failures

### Understanding error vs warn

Assertions can `'error'` (fail build) or `'warn'` (log only):

```js
// lighthouserc.js
const config11 = {
  ci: {
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }], // Fail if < 90
        'first-contentful-paint': ['warn', { maxNumericValue: 2000 }], // Log if > 2s

        // Use warn for flaky metrics
        'speed-index': ['warn', { maxNumericValue: 4000 }],

        // Use error for critical issues
        'errors-in-console': ['error', { maxLength: 0 }]
      }
    }
  }
}
```

### Setting realistic budgets

**Problem:** Assertions constantly fail, blocking PRs

**Solution:** Set budgets based on current performance, then gradually improve:

```bash
# Get baseline scores
lhci collect --url=http://localhost:8080
lhci assert --preset=lighthouse:no-pwa

# Set budgets 10% better than current
# If current performance score is 75, set minScore: 0.7
```

Start with `preset: 'lighthouse:recommended'` and relax failing assertions until stable, then tighten over time.

**Recommended approach:**

1. Start with warnings only
2. Collect 1 week of data
3. Set error thresholds at P90 (90th percentile)
4. Gradually lower thresholds

See [GitHub Actions setup](/learn-lighthouse/lighthouse-ci/github-actions) and [GitLab CI setup](/learn-lighthouse/lighthouse-ci/gitlab-ci) for working examples.

## Next Steps

Once your CI setup is working:

- **Understand what you're measuring:** [Core Web Vitals overview](/learn-lighthouse/core-web-vitals) explains the performance metrics
- **Debug specific issues:** Use [LCP Finder](/tools/lcp-finder), [CLS Debugger](/tools/cls-debugger), or [INP Analyzer](/tools/inp-analyzer) to diagnose failures
- **Fix the issues:** Look at [LCP](/learn-lighthouse/lcp), [CLS](/learn-lighthouse/cls), and [INP](/learn-lighthouse/inp) optimization guides
- **Set realistic thresholds:** Use [budgets](/learn-lighthouse/lighthouse-ci/budgets) to define performance standards without blocking every PR

## PROTOCOL_TIMEOUT Errors

**Problem:** Chrome hangs during audit with `PROTOCOL_TIMEOUT` error

**Solution:** This is a [known unfixable issue](https://github.com/GoogleChrome/lighthouse-ci/issues/428) - Chrome occasionally hangs and nothing can be done on the Lighthouse side. The only solution is to retry:

```js
// In CI, wrap with retry logic
// Or increase numberOfRuns and accept occasional failures
const config12 = {
  ci: {
    collect: {
      numberOfRuns: 5, // More runs = better chance of success
    }
  }
}
```

## Chrome Headless Mode Changes

**Problem:** Deprecation warnings about `--headless` or crashes with Chrome 132+

**Solution:** [Chrome 132 removed `--headless=old`](https://developer.chrome.com/blog/removing-headless-old-from-chrome) from the main binary. LHCI 0.14+ uses `headless: "new"` by default. If you need old headless behavior, use the separate `chrome-headless-shell` binary.

## Still Having Issues?

- Check [GoogleChrome/lighthouse-ci GitHub Issues](https://github.com/GoogleChrome/lighthouse-ci/issues)
- Review [Lighthouse CI documentation](https://github.com/GoogleChrome/lighthouse-ci/blob/main/docs/getting-started.md)
- Verify [configuration reference](/learn-lighthouse/lighthouse-ci/configuration) syntax
- Enable verbose logging: `lhci autorun --verbose`
