## TL;DR
A 401 in CI with a 200 locally is almost always a missing or empty API credential in the CI environment, not a Playwright bug. Log whether the credential is present (never its value) at the start of the CI job. Fix the secret wiring, and the same code passes in both places.

## Error
```text
Error: expect(response.ok()).toBeTruthy() failed
Response status: 401 Unauthorized
Request: GET https://api.example.com/v1/orders
```

## Steps
1. Add a CI step that prints whether the secret is set without printing its value, for example a shell check on the env var that echoes "token present" or "token MISSING". Expected: you see which side of the wiring is broken.
2. Check the CI secret name matches exactly what the test reads (case, underscores, no trailing spaces). Expected: the names match character for character.
3. Verify the request sends the header the API expects, with the credential read from the environment at runtime. Expected: the request shape matches your passing local run.
4. Re-run CI. Expected: 200, same as local.

## When to use
- The same Playwright API test returns 200 locally and 401 in CI.
- You just added a new secret or renamed an env var.
- The failure started right after a CI config change.

## When not to use
- The 401 happens locally too (then the credential itself is wrong or expired).
- You are debugging an OAuth login flow rather than a static API credential.
- The API returns 403 (authorized but forbidden: a scopes problem, not a missing credential).

## Tool compatibility
- Playwright 1.40 through 1.5x `APIRequestContext` (`request` fixture).
- Any CI system with secret env vars: GitHub Actions, GitLab CI, Jenkins, CircleCI.

## Variant phrasings
### playwright API request unauthorized only in CI
Same credential-wiring cause; check the secret mapping first.
### request context 401 github actions but works locally
Same pattern; verify the Actions secret is mapped into the job env.

## Why it happens
Local runs read the credential from a shell profile or env file that does not exist in CI. CI secrets must be mapped into the job environment explicitly, and a missing mapping sends an empty or absent auth header, which the API rejects with 401.

## Edge cases
- Token present but still 401: the CI token may be expired, scoped differently, or issued for the wrong environment.
- 401 on some requests only: the token is fine but the baseURL differs between local and CI.
- Works on retry: suspect clock skew on short-lived tokens, but check expiry first.

## Provenance

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