# pa11y error unable to load url connection timeout - how to fix it

## TL;DR

Verify the URL is reachable from the scan environment: curl it from the same machine or container, check DNS and proxy settings, then pass the reachable URL to pa11y. Nine times out of ten the scanner cannot reach the host at all. One line of why: pa11y wraps Chromium network errors, and unable to load URL with a connection timeout means the browser never got a single byte.

## The error, verbatim

```text
Error: Unable to load URL: connect timed out
    url: https://staging.example.com/app

```

## Fix it step by step

### Step 1: Reproduce the network failure

```bash
curl -sS -o /dev/null -w '%{http_code}\n' --max-time 20 https://staging.example.com/app
```

Expected: curl also times out or fails, proving the scanner is not the problem.

### Step 2: Check DNS resolution

```bash
node -e "require('dns').lookup('staging.example.com', function(e,a){console.log(e?e.message:a)})"
```

Expected: Shows whether the hostname resolves in the scan environment.

### Step 3: Check proxy requirements

```bash
env | rg -i 'proxy' | sed 's/=.*/=.../'
```

Expected: Reveals proxy env vars; CI containers often need a proxy pa11y does not inherit.

### Step 4: Scan the reachable URL

```bash
npx pa11y https://staging.example.com/app --reporter cli | tail -3
```

Expected: Scan starts and reports violations once the URL is reachable.

### Step 5: Re-run twice to rule out flakes

```bash
npx pa11y https://example.com/ | tail -2
```

Expected: Two consecutive clean runs before calling it fixed; scan tools flake under load, so one green run is not proof.

## When to use this skill

- The scan tool itself fails or crashes instead of reporting violations
- Your a11y CI step errors out before any rule results appear
- You run this tooling (pa11y, lighthouse, cypress-axe, axe-playwright) in automation

## When NOT to use this skill

- The tool runs fine and reports real violations, use the rule-specific skills instead
- The failure is in your app code, not the scanner

## Compatibility

pa11y 6.x / pa11y-ci 3.x. The fix is environmental: DNS, proxy, firewall, or VPN. Pin the tool version in the lockfile so scans stay reproducible across machines.

## Variant phrasings

### pa11y unable to load url

Same error, same network triage.

### pa11y connect timed out staging

The classic: staging is VPN-only and CI has no VPN route.

### same failure locally and in CI

Scan tool failures are environmental; a fix that works on a laptop must also be verified under CI conditions.

## Why it happens

Pa11y runs Chromium, which uses the system network stack of wherever pa11y runs. Staging URLs behind VPNs, DNS that only resolves on the office network, and CI containers without proxy env vars all produce the same unable to load URL error. The error message does not distinguish DNS failure from TCP timeout from TLS failure, so triage with curl first to see which layer is broken. Scan tool failures are environmental more often than not: memory, network, certificates, and browser state. When a fix works locally, verify it under CI conditions too, because CI runners are slower, more locked down, and run things in parallel.

## Edge cases

- If the site needs client certificates or VPN, run pa11y where a browser can actually reach it, not in a locked-down CI container.
- HTTP basic auth on staging needs --headers or a config file, a 401 is not a timeout but looks similar at first glance.
- IPv6-only CI networks failing to reach IPv4 hosts produce timeouts, check with curl -4 vs curl -6.
- Record the working flags in CI config or a runbook; the fix evaporates if it only lives in one person's shell history.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_vH1QDjom_EdJsQ6dGyaLxQ
