Between Sep 15 and Sep 18, 2026, at least seven Cloudflare Community threads had the same shape. An account-owned token had Individual Workers, Editor on one existing Worker. tokens/verify said active. Then wrangler deploy stopped on its first API call with Wrangler authentication error 10000.
I answered one of those threads on Sep 18. That afternoon a Cloudflare staff member posted that they'd fixed an issue behind it. The platform bug is gone, but the same error still shows up for reasons that are your config, and the checks below tell the two apart.
What happened between Sep 15 and Sep 18
The Workers roles and permissions page, which introduced per-Worker roles, was updated on Sep 15. Its Wrangler table says deploying an existing Worker needs Editor for that Worker, nothing more. People built tokens exactly that way and hit 403 with code 10000.
| When (UTC) | What |
|---|---|
| Sep 15 | Per-Worker roles documented. The first threads with 403 / 10000 appear |
| Sep 17, 09:25 | Staff reply in Individual Worker Editor token rejected with error 10000: can't reproduce, and posts a successful response from a per-Worker Editor token |
| Sep 18, 14:50 | Same thread: "We've fixed an issue that I believe affected this request" |
If your token dates from that week and still fails, re-run the same request once before touching the token. If it now works, you were caught by that bug.
Why does tokens/verify say active when the deploy fails?
Because verify doesn't check permissions. Cloudflare's create token guide says the endpoint fetches the current status of the token. That tells you it exists and isn't expired or revoked. A 200 there and a 403 on a Worker aren't a contradiction, and verify can't tell you whether a scope is right.
There's also a second trap: there are two verify endpoints. User-owned tokens use /user/tokens/verify, and account-owned tokens use /accounts/<id>/tokens/verify. I tried both with a user-owned token I keep for analytics reads. The first returned "status": "active". The second returned 1000 Invalid API Token for the same token, same account.
The first API call wrangler deploy makes
In the workers-sdk source, validate-worker-props.ts reads the Worker's metadata from /accounts/<id>/workers/services/<name> before it uploads anything. That's where the threads failed, so it's the request to reproduce by hand:
curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/services/$WORKER" \
-H "Authorization: Bearer $CF_TOKEN" | jq '{success, errors}'
With a working per-Worker Editor token this returns "success": true and the Worker's metadata. With code 10000 it's worth comparing against the successful response posted in the staff reply before rebuilding the token again.
The --env suffix: a name the token was never given
A per-Worker role is granted to a Worker name, and wrangler deploy --env production doesn't deploy the name at the top of your config. It deploys <name>-production.
My own home page Worker is app-home in wrangler.jsonc and deploys with --env production. Running the curl above on 2026-09-30 with a token that has no Workers permission at all gave three different answers:
| Name requested | Response |
|---|---|
app-home | 10090 This Worker does not exist on this account. |
app-home-production | No access to the specified service. |
| A made-up name | 10090 This Worker does not exist on this account. |
So "does not exist" can mean you asked for the unsuffixed name, and "no access" means the Worker exists but your token isn't allowed near it. If you scoped the token in the dashboard by picking the Worker from a list, check that you picked the suffixed one.
Four things a per-Worker Editor token still can't do
The same docs page lists the gaps. They're the reasons a correctly scoped token can still fail on the day the platform is fine:
| Action | What it needs |
|---|---|
| Create a Worker that doesn't exist yet | Product-level Admin. Per-Worker roles can't apply to a Worker that isn't there |
| Add, change or remove Routes or Custom Domains | Editor on the Worker plus Zone, Workers Routes, Write on every affected zone |
| Custom Domains with per-Worker roles | Not supported yet. The Limitations section says support is planned |
| Query D1, read KV keys, list R2 objects directly | Permissions on that resource. Deploying with bindings doesn't need them |
The second row catches deploys that look harmless. If your wrangler config lists routes or a custom_domain, a deploy that changes them needs the zone permission too. The docs say later deploys only need Editor, as long as they don't add, update or remove that connection.
A minimal CI token for one Worker
For a deploy-only pipeline I'd build the token from the table up. Start with Individual Workers, Editor on the exact deployed name, suffix included. Add Zone, Workers Routes, Write on the zone only if the pipeline changes routes. Add nothing for bindings unless a step queries D1 or KV directly.
My own home page deploy token is older than per-Worker roles and has Workers Routes and DNS edit on the zone, because its config binds two Custom Domains. I can't shrink it to one Worker yet, and that's the third row of the table in practice. Scoped tokens with an expiry are also item 11 in my Cloudflare zone security checklist, which has the read-only command for listing the tokens you already have.