## TL;DR

Wrangler deploy auth errors come from three places: a missing token, a token without the right permissions, or a stale cached session overriding a good token. Check which credential wrangler is actually using with `wrangler whoami`, create a fresh token with Workers edit permissions if needed, and pass it via the `CLOUDFLARE_API_TOKEN` environment variable in CI. Never paste a real token into logs or config files.

## The query

```text
wrangler deploy error: authentication error, check your API token
```

## Use this when

- `wrangler deploy` fails with an authentication error mentioning the API token.
- Deploys worked before and broke after a token rotation or permission change.
- CI deploys fail while local deploys work (or the reverse).

## Not for

- "You need to provide a valid account id" (that is an account targeting problem, separate skill).
- `wrangler login` failing to open a browser in headless CI (use token auth instead).
- Deploys rejected for plan limits or script size (those are quota problems).

## Steps

### Step 1: See which credential wrangler is using

```bash
npx wrangler whoami
```

Expected output: the current identity (OAuth user or token). If this errors, wrangler has no working credential at all, and everything downstream is explained.

### Step 2: Check whether a token is set in the environment

```bash
if [ -n "$CLOUDFLARE_API_TOKEN" ]; then echo "token is set"; else echo "token is missing"; fi
```

Expected output: "token is set" or "token is missing." This only reports presence, never the value. Wrangler prefers the environment token over any cached OAuth session, so a stale token here silently overrides a fresh login.

### Step 3: Clear a stale cached session

```bash
npx wrangler logout
npx wrangler whoami
```

Expected output: after logout, `whoami` fails cleanly instead of reporting a dead identity. This removes the cached OAuth session that was shadowing the environment token (or vice versa).

### Step 4: Create a fresh token with the right permissions

```text
In the Cloudflare dashboard: Manage Account, API Tokens, Create Token.
Use the Workers template or a custom token with:
- Account / Workers Scripts / Edit
- Account / Workers KV Storage / Edit (if the worker uses KV)
- Zone / Workers Routes / Edit (if the deploy manages routes)
```

Expected output: a new token string shown once. Copy it into the CI secret store immediately. The most common permission miss is Workers Scripts Edit without the storage or routes scopes the deploy also touches.

### Step 5: Provide the token via the environment in CI

```bash
export CLOUDFLARE_API_TOKEN
```

Expected output: the variable is exported (its value comes from the CI secret store, never from a file or chat). Wrangler picks it up automatically on the next command.

### Step 6: Verify and redeploy

```bash
npx wrangler whoami && npx wrangler deploy
```

Expected output: `whoami` shows the token's identity with the right account, and the deploy proceeds. If auth still fails, the token's permissions do not cover something the deploy touches. Re-check step 4's scope list against the worker's bindings.

## Variant phrasings

### wrangler login failed: browser did not open in headless environment
Do not fight the browser flow in CI. Steps 4-5 (token via environment) are the headless auth path.

### wrangler whoami not authenticated after token rotation
The old token is dead and something still references it. Steps 2-3 find where, step 4 replaces it.

### wrangler tail: authentication failed on first connect
Same credential problem surfacing in tail instead of deploy. Fix the token once and both work.

## Why it happens

Wrangler has two credential sources (cached OAuth session and environment token) with a precedence order the agent does not see. A revoked token in the environment silently overrides a fresh `wrangler login`, and a stale OAuth session survives token rotation. The agent keeps "fixing" auth by logging in again while the broken credential sits in the other source, untouched. `whoami` plus the presence check in step 2 make the invisible precedence visible.

## Edge cases

- Tokens are shown once at creation. If it was not saved to the secret store, create a new one. There is no way to reveal an existing token.
- The Workers template token covers scripts but not always KV, R2, or routes. Match the token scopes to the worker's bindings or the deploy fails halfway.
- `wrangler logout` clears the OAuth session but never touches the environment variable. Both sources must be checked, which is why steps 2 and 3 are separate.
- A token scoped to the wrong account authenticates fine and then fails on deploy with confusing errors. Confirm the account in step 6, not just the auth success.
- Never echo the token value for debugging. The presence check in step 2 is the safe version of "is my token set."

## Provenance

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