wrangler-accounts

star 1

AWS-style multi-account convenience for Cloudflare Wrangler. Use when you need to run wrangler commands against a specific Cloudflare account, manage saved OAuth profiles, set or switch the persistent default profile, or open an isolated subshell for a profile. Prefer --json for machine-readable output.

leeguooooo By leeguooooo schedule Updated 4/22/2026

name: wrangler-accounts description: AWS-style multi-account convenience for Cloudflare Wrangler. Use when you need to run wrangler commands against a specific Cloudflare account, manage saved OAuth profiles, set or switch the persistent default profile, or open an isolated subshell for a profile. Prefer --json for machine-readable output.

Wrangler Accounts

Overview

wrangler-accounts runs wrangler under per-invocation shadow HOME isolation, so multiple shells can use different Cloudflare accounts in parallel without any global switching. Profile resolution order: --profile / -p > positional shorthand > $WRANGLER_PROFILE > profilesDir/default > hard error.

Installation

For Claude Code users, prefer the plugin marketplace install path because it ships this skill and the raw-wrangler guard hook together:

/plugin marketplace add leeguooooo/wrangler-accounts
/plugin install wrangler-accounts@leeguoo-tools

The plugin does not replace the CLI binary. The actual wrangler-accounts executable still must be installed on PATH:

npm i -g github:leeguooooo/wrangler-accounts

Non-Claude-Code users can keep using the skills.sh distribution path for this same SKILL.md mirror:

npx skills add leeguooooo/wrangler-accounts -g -y

If a Claude Code user previously installed via skills.sh, removing the standalone copy avoids a duplicate /wrangler-accounts entry in the command picker:

npx skills remove wrangler-accounts
# or: rm -rf ~/.agents/skills/wrangler-accounts

Prerequisites (check before running any recipe below)

This skill is only documentation — the actual wrangler-accounts binary must also be installed on the user's PATH. Before running any command below, verify:

command -v wrangler-accounts && wrangler-accounts --version

If the command is missing, tell the user to install the CLI first:

npm i -g github:leeguooooo/wrangler-accounts

wrangler itself (the Cloudflare CLI) must also be on PATH. If missing:

npm i -g wrangler

Minimum versions you should ask the user to upgrade past

If wrangler-accounts --version is below any of these, upgrade first before debugging anything else — older versions have real bugs that will misdirect you:

Version What it fixed Symptom on older versions
≥ 1.2.2 per-profile WRANGLER_CACHE_DIR, fixes account-id leak across profiles d1/r2 object commands return 7403 not authorized even though OAuth is valid; deploys silently land in the wrong account
≥ 1.3.0 STATUS column distinguishes valid / valid* / EXPIRED (refresh-token-aware) list shows EXPIRED for healthy profiles, scaring you into running login for no reason
≥ 1.4.0 login refuses non-TTY contexts and accidental overwrites login <name> hangs forever in non-interactive contexts; reflexive login overwrites a healthy profile
≥ 1.6.0 API token profiles (token-add) + anonymous env-var pass-through only OAuth profiles existed; CLOUDFLARE_API_TOKEN in env still required a named profile to be selected
npm i -g github:leeguooooo/wrangler-accounts    # reinstall from main = latest

Triage flow — when something looks wrong

Run these in order before reaching for login or any destructive command. The agent who skipped this step in a recent incident ended up overwriting a perfectly healthy profile in a sub-shell where the browser couldn't even open.

# 1. Verify your binary is recent enough (see version table above)
wrangler-accounts --version

# 2. Authoritative state of every profile (hits Cloudflare API per profile)
wrangler-accounts list --deep

How to read list --deep output:

You see Meaning What to do
STATUS valid + VERIFIED ✓ ok profile is fine nothing — proceed with whatever the user actually asked
STATUS valid* + VERIFIED ✓ ok profile is fine; access token will auto-refresh on next use nothing — valid* is healthy, do NOT re-login
STATUS EXPIRED + VERIFIED ✓ ok rare; only on < 1.3.0 binaries — STATUS is lying upgrade the CLI; profile is fine
STATUS EXPIRED + VERIFIED ✗ Not logged in profile is genuinely broken wrangler-accounts login <name> (interactive only)
STATUS valid + VERIFIED ✗ of any kind refresh token revoked server-side wrangler-accounts login <name> (interactive only)
STATUS unknown profile file lacks expiration_time run --deep (already did) — trust VERIFIED

Rule: only suggest wrangler-accounts login <name> when at least one of:

  1. list --deep showed VERIFIED ✗ for that profile, OR
  2. The profile doesn't exist at all yet, OR
  3. The user explicitly says "re-authenticate" / "log me in again"

If list --deep shows everything ✓ but a wrangler command still fails, the root cause is somewhere other than wrangler-accounts — most likely:

  • Project-local ./.wrangler/state/ is sharing data across profiles (see "What is and isn't isolated")
  • The project's wrangler.toml has the wrong account_id hardcoded
  • The user is on a CLI version below 1.2.2 (account-id cache leak)
  • The wrangler subcommand actually needs --remote or --local and you forgot

Quick Start

  • wrangler-accounts login <name> — interactive OAuth login into a new profile (never touches real ~/.wrangler)
  • wrangler-accounts token-add <name> <api-token> <account-id> — save an API token profile (no browser login needed)
  • wrangler-accounts default <name> — set the persistent default profile
  • wrangler-accounts deploy — run wrangler deploy under the default profile
  • wrangler-accounts --profile personal deploy — one-shot override
  • wrangler-accounts exec work -- npm run release — run a command in an isolated subshell for the work profile

💡 Reading the STATUS column: valid = healthy, valid* = healthy (will auto-refresh on next use, don't re-login), EXPIRED = truly broken (need login), unknown = run --deep to find out. Full table below in "List and inspect profiles".

Tasks

Run wrangler against a profile

Per-invocation (preferred for scripts):

wrangler-accounts --profile <name> <wrangler-args...>

Or with env var:

WRANGLER_PROFILE=<name> wrangler-accounts <wrangler-args...>

Or positional shorthand (only when <name> is a saved profile name, not a management subcommand):

wrangler-accounts <name> <wrangler-args...>

Open a subshell for a profile

wrangler-accounts exec <name> — launches $SHELL -i with isolated HOME and WRANGLER_PROFILE set. Everything inside the subshell sees the profile, including nested npm run scripts, Makefiles, and npx wrangler.

Run a single command instead:

wrangler-accounts exec <name> -- <cmd> [args]

Manage the persistent default profile

  • wrangler-accounts default — print current default (exit 1 if none set)
  • wrangler-accounts default <name> — set the default
  • wrangler-accounts default --unset — clear the default
  • wrangler-accounts default --json — JSON output

Show the resolved identity for a profile

wrangler-accounts whoami [--profile <name>] — reports the profile name, source tier (cli / positional / env / default), and identity from meta.json. Does not spawn wrangler.

Use --json for structured output.

List and inspect profiles

  • wrangler-accounts list — text table with NAME / STATUS / EXPIRES / IDENTITY columns
  • wrangler-accounts list --json — structured: array of {name, isDefault, isActive, status, expirationTime, hasRefreshToken, identity, verified, verifyError}
  • wrangler-accounts list --plain — one profile name per line (scriptable)
  • wrangler-accounts list --deepauthoritative check: spawns wrangler whoami in a shadow HOME for every profile and reports whether Cloudflare actually accepts the credentials. Slower (makes network calls), but the only way to catch revoked refresh tokens or broken profile files.
  • wrangler-accounts status / status --json
  • Pass --include-backups to show hidden backup profiles.

STATUS values (1.3.0+):

value meaning user action
valid access_token is currently valid none
valid* / refreshable access_token past expiry BUT refresh_token present; wrangler will auto-refresh on next use none — this is fine, don't scare the user
EXPIRED / expired access_token expired AND no refresh_token saved; profile is genuinely broken wrangler-accounts login <name>
unknown profile file has no expiration_time field run list --deep to verify live
token API token profile (1.6.0+) — no expiration concept, always ready none

Cloudflare OAuth lifecycle reference: access tokens are short-lived (1 hour) by design. Every profile with offline_access in its scopes also has a long-lived refresh_token (30 days, silently extended on use). Wrangler refreshes access tokens automatically whenever it runs a command and the current one is past expiry. Do not tell the user to re-login just because list shows an expired access token — check hasRefreshToken first. If the profile's STATUS is valid* / refreshable, nothing is wrong.

The only time a user actually needs wrangler-accounts login <name> again is:

  1. STATUS is EXPIRED (no refresh_token at all — profile was saved without offline_access scope)
  2. OR list --deep returns with "Not logged in" / "refresh token may be revoked" (refresh token itself got invalidated)

Save an API token profile (no browser required)

wrangler-accounts token-add <name> <api-token> <account-id> [--force]

Saves a Cloudflare API token + account ID as a named profile. No OAuth browser flow needed. The credentials are stored in token.json (mode 0600) inside the profile directory.

# Get your API token from: Cloudflare dashboard → My Profile → API Tokens
wrangler-accounts token-add work CF_TOKEN_HERE ACCOUNT_ID_HERE

# Use identically to OAuth profiles
wrangler-accounts --profile work deploy
wrangler-accounts work r2 list

Token profiles appear in list with a [token] type indicator and STATUS: token — there is no expiration concept, so they are always ready to use. remove works the same as for OAuth profiles.

Env-var pass-through (1.6.0+): when CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are already set in the environment and no profile is specified, wrangler-accounts runs in anonymous-token mode (no named profile needed). Useful for CI jobs that inject credentials via secrets:

CLOUDFLARE_API_TOKEN=xxx CLOUDFLARE_ACCOUNT_ID=yyy wrangler-accounts deploy

Save, sync, login, remove

  • wrangler-accounts save <name> — snapshot current Wrangler config as a profile
  • wrangler-accounts sync <name> — refresh a specific profile from the current login
  • wrangler-accounts sync-default — refresh the default profile
  • wrangler-accounts login <name> — fresh isolated OAuth login
  • wrangler-accounts remove <name> — delete a profile (works for both OAuth and token profiles)

Clean up stale shadow HOMEs

wrangler-accounts gc [--older-than 1h] — removes wa-* directories under $TMPDIR older than the threshold (default 1h). Safe to run at any time.

Install the global wrangler shim (enforce isolation for any caller)

wrangler-accounts shim install [--apply] — installs a wrangler shim that, once it's at the front of PATH, intercepts every bare wrangler <args> call and blocks it with guidance (telling the caller to use wrangler-accounts --profile <name> ...) whenever profiles are configured. This makes the "use wrangler-accounts instead of raw wrangler" rule apply to any caller — Codex, Cursor, a plain terminal, a shell script — not just Claude Code (whose plugin hook already does this at the tool layer).

IMPORTANT — this is a separate opt-in step. npm i -g ... installs the CLI but does not install the shim (it changes PATH, so it's never automatic). And after shim install, two more things must be true before a bare wrangler is actually blocked: (1) the shim dir is ahead of the real wrangler on PATH, and (2) at least one profile is configured. If a user says "I installed it but wrangler isn't being intercepted", run wrangler-accounts shim status first — the usual cause is Active: no (PATH not updated / shell not restarted).

Full setup (the three steps a user needs):

npm i -g github:leeguooooo/wrangler-accounts   # 1. CLI
wrangler-accounts shim install --apply          # 2. shim + PATH edit (zsh/bash/fish)
# 3. open a new shell, then:
wrangler-accounts shim status                   # want: Active (intercepts bare wrangler): yes
  • wrangler-accounts shim install — writes the shim to ~/.wrangler-accounts/shims/ and prints the exact PATH line for the user's shell (export PATH=... for zsh/bash, fish_add_path -p ... for fish).
  • wrangler-accounts shim install --apply — also writes that line into the detected rc file, idempotently: ~/.zshrc, ~/.bashrc, or ~/.config/fish/config.fish (fish is fully supported).
  • wrangler-accounts shim status — reports whether the shim is installed and active (i.e. ahead of the real wrangler on PATH). If status says installed-but-not-active, the shim dir isn't early enough on PATH (tell the user to open a new shell or re-run with --apply).
  • wrangler-accounts shim uninstall [--apply] — removes the shim (and the rc line with --apply).

The shim passes through (runs real wrangler) in these cases, so it never gets in the way:

  • WA_PASSTHROUGH=1 wrangler <args> (explicit one-off escape; NOWRANGLER_ACCOUNTS_GUARD=1 also honored)
  • account-agnostic commands: wrangler --version / --help
  • when no profiles are configured, or wrangler-accounts isn't installed
  • inside wrangler-accounts exec / any wrangler-accounts-spawned subprocess (already isolated)

Limitation: npx wrangler / pnpm wrangler / ./node_modules/.bin/wrangler resolve a project-local binary that a PATH shim cannot shadow — same gap the Claude Code hook already exempts. The shim covers globally-invoked wrangler only.

The shim and the Claude Code plugin hook are complementary: the hook fires earlier (Claude's tool layer, gives the model exit 2 feedback); the shim enforces the same rule at the exec layer for every other agent and shell. Both can be installed at once.

Common Recipes

These are the patterns the user is most likely asking about when they mention "Cloudflare accounts", "wrangler", or "multi-account deploys". Pick the one that matches intent.

User wants: deploy a worker to a specific account

wrangler-accounts --profile work deploy

Or, when the user will be on this account for a while:

wrangler-accounts default work          # set once
wrangler-accounts deploy                # uses work from now on
wrangler-accounts deploy --env staging  # wrangler flags pass through unchanged

User wants: tail production logs on one account while developing on another

# shell 1
wrangler-accounts --profile work tail my-worker --format pretty

# shell 2 (simultaneously, zero interference)
wrangler-accounts --profile personal dev

The two shells each get their own shadow HOME, so there's no global state for the other to clobber.

User wants: run a deploy script / npm script / Makefile against a specific account

wrangler-accounts exec work -- npm run deploy
wrangler-accounts exec work -- make release
wrangler-accounts exec work -- bash scripts/deploy.sh

Anything inside the subshell that calls wrangler (directly, via npx, via pnpm, via a package.json script) automatically uses the work profile.

User wants: set up a new Cloudflare account from scratch

wrangler-accounts login new-account    # opens the browser OAuth flow
wrangler-accounts whoami --profile new-account   # verify identity
wrangler-accounts list                 # confirm the profile is saved

The login flow runs inside an isolated shadow HOME, so the user's real ~/.wrangler/config/default.toml is never touched.

⚠️ login is destructive. It opens a browser, requires the user to click "Authorize" interactively, and OVERWRITES the named profile. As of 1.4.0, wrangler-accounts login <name> refuses to run if (a) stdin is not a TTY, or (b) the profile already exists and looks healthy — both unless you pass --force. Never run login to "verify" or "refresh" a profile — see the antipattern below.

❌ Antipattern: running login to verify a profile works

This is wrong:

wrangler-accounts login Xdreamstar2025   # ❌ DON'T do this just to check

Reasons:

  1. login is destructive — it overwrites the saved profile with a brand new OAuth flow.
  2. login requires a browser and an interactive terminal — it cannot complete in a Bash sub-shell, CI runner, or sub-agent context. The command will hang waiting for the user.
  3. The Cloudflare access token in a healthy profile auto-refreshes via refresh_token — there is nothing to "log in to" when the profile already works.

Use one of these instead:

wrangler-accounts whoami --profile Xdreamstar2025   # fast, reads meta.json, no network
wrangler-accounts list --deep                       # authoritative, runs wrangler whoami per profile
wrangler-accounts list                              # quick STATUS overview (valid / valid* / EXPIRED / unknown)

Only fall back to wrangler-accounts login <name> when:

  • The profile does not exist yet (creating a new account profile from scratch)
  • The profile shows EXPIRED (truly expired, no refresh_token left) — see STATUS table above
  • list --deep returns with "Not logged in" / "refresh token may be revoked" (server-side revocation)
  • The user explicitly says "re-authenticate this profile" / "log me in again"

User wants: check which account a profile is tied to, without running wrangler

wrangler-accounts whoami --profile <name>           # text
wrangler-accounts whoami --profile <name> --json    # structured

Returns the email + account ID from the saved meta.json. No network call.

User wants: swap between many accounts quickly in one shell

Use default as a "current" setting:

wrangler-accounts default work
wrangler-accounts deploy               # work
wrangler-accounts default personal
wrangler-accounts dev                  # personal

Or use positional shorthand inline:

wrangler-accounts work deploy          # one-shot, no persistent default
wrangler-accounts personal dev

User wants: run wrangler in CI with multiple accounts

Don't use wrangler-accounts in CI. Use native env vars:

# In CI secrets:
CLOUDFLARE_API_TOKEN=<token-with-workers-deploy-scope>
CLOUDFLARE_ACCOUNT_ID=<account-id>

# In the pipeline:
wrangler deploy

wrangler-accounts is for local developer OAuth sessions. CI should use long-lived API tokens directly with plain wrangler. Recommend this even if the user asks to use wrangler-accounts in CI.

Troubleshooting

"Profile 'X' has expired Wrangler OAuth credentials and no refresh_token to renew them"

The saved OAuth access_token is past its expiration_time AND there is no refresh_token in the profile config (no offline_access scope at login time, or the token was revoked). The profile is genuinely broken; only re-authenticating fixes it:

wrangler-accounts login <name>

This overwrites the existing profile with a fresh OAuth session. Any saved metadata (identity, etc.) is re-verified.

Note: If you see this error in 1.5.0 or earlier, you may be hitting a known regression: any profile whose access_token had passed expiration was blocked even when a refresh_token was present. Upgrade to 1.5.1+ — wrangler itself silently refreshes those tokens on the next call, so wrangler-accounts no longer pre-flights against expiration_time alone.

"No profile specified. Options: ..."

The user ran wrangler-accounts <wrangler-args> without a resolvable profile. Fix one of:

wrangler-accounts --profile <name> <args>        # one-shot
WRANGLER_PROFILE=<name> wrangler-accounts <args> # env var
wrangler-accounts default <name>                 # persistent default

1.6.0+: if CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are both present in the environment, this error no longer fires — the tool runs in anonymous-token mode automatically.

"Profile not found: X"

The profile name doesn't exist in profilesDir. Check what's saved:

wrangler-accounts list

Create it with wrangler-accounts login <name> or copy an existing Wrangler login with wrangler-accounts save <name>.

Inside wrangler-accounts exec, cd ~ lands in a weird tmpdir

Expected. Inside an exec session, $HOME is the shadow HOME. The real home is still accessible:

cd "$WRANGLER_ACCOUNT_REAL_HOME"

Or users can add a shell alias: alias realhome='cd "$WRANGLER_ACCOUNT_REAL_HOME"'.

Unsupported use command migration

wrangler-accounts use <name> no longer switches profiles. It exits with migration guidance because the old behavior was ambiguous and rewrote Wrangler's global config. Suggest the replacement based on intent:

  • "I want this account to stick for a while" → wrangler-accounts default <name>
  • "Just this one command" → wrangler-accounts --profile <name> <wrangler-args>
  • "I want an interactive shell on that account" → wrangler-accounts exec <name>

[ERROR] A request to the Cloudflare API ... Authentication error [code: 10000] with code: 7403 ("not authorized to access this service")

The OAuth token is fine but the URL contains the wrong account ID. wrangler caches the user's selected account in wrangler-account.json. If that cache file is shared across profiles, profile A's OAuth token gets paired with profile B's cached account ID, sending API calls to the wrong account. Symptoms:

  • deploy and secret put succeed (they don't put account ID in the URL path)
  • d1 execute --remote, r2 object get/put, anything else with /accounts/<id>/... in the URL fails with 7403

Fix path (in order):

  1. Are you on wrangler-accounts ≥ 1.2.2? Run wrangler-accounts --version. If < 1.2.2, upgrade — earlier versions pointed WRANGLER_CACHE_DIR at a shared global path. 1.2.2 isolates the cache per profile.
  2. Clear the polluted shared cache (one-time, even after upgrading):
    rm -f ~/.wrangler/cache/wrangler-account.json
    rm -f ~/Library/Preferences/.wrangler/cache/wrangler-account.json   # macOS env-paths fallback
    
  3. Verify with wrangler-accounts list --deep — the VERIFIED column for each profile should be ✓ ok. If , the underlying OAuth session itself is broken; run wrangler-accounts login <name>.
  4. Defense in depth: set CLOUDFLARE_ACCOUNT_ID=<correct-id> in the calling environment. wrangler reads this env var directly and bypasses the cache entirely. Useful for scripts or one-off recovery commands:
    CLOUDFLARE_ACCOUNT_ID=<id> wrangler-accounts <profile> r2 object put ...
    

What to tell the user: "wrangler returned 7403 because it cached the wrong account ID alongside your OAuth token. This was a real bug in wrangler-accounts ≤ 1.2.1 (shared cache directory across profiles). Upgrade to 1.2.2 and clear the polluted cache."

"The OAuth config seems right, but the wrong account is being used"

Same root cause as the 7403 above. Default to the same fix path.

wrangler dev (or any --local command) shows stale data after switching profiles

Project-local state at <project>/.wrangler/state/ is NOT isolated per profile — wrangler's getLocalPersistencePath (cli.js:149025) hardcodes the path next to wrangler.toml and the only override is the --persist-to CLI flag (no env var hook). So if profile A's wrangler dev populated a local D1 emulation, then you switch to profile B and run wrangler dev in the same directory, B sees A's emulated rows.

This only affects --local simulations. --remote commands hit Cloudflare directly and are unaffected — that's the common case for d1/r2 work in a multi-account setup.

Two clean fixes:

  1. Use git worktrees (recommended for any serious multi-profile dev workflow):
    git worktree add ../my-project-work main
    git worktree add ../my-project-personal main
    cd ../my-project-work && wrangler-accounts exec work     # isolated .wrangler/state/
    cd ../my-project-personal && wrangler-accounts exec personal
    
  2. Clear state manually before switching:
    rm -rf .wrangler/state
    wrangler-accounts --profile <new> dev
    

wrangler-accounts does not auto-isolate .wrangler/state/ because the only mechanism would be argv injection of --persist-to, which has too many failure modes (different subcommands accept persistTo at different positions, can't override user-supplied flags, path selection is ambiguous between per-profile and per-profile-per-project). The honest tradeoff is documented in the "What is and isn't isolated" table above — partial isolation with hidden gotchas would be worse than honest sharing.

Shell history / .zsh_history seems to grow when running exec

Intentional. By design the shadow HOME symlinks all top-level entries of real HOME except .wrangler, so shell history writes pass through to the real file. This is a convenience bias, not a clean-room sandbox — the goal is that exec subshells feel like a normal terminal with a different Cloudflare account, not a jail.

Invariants the AI should rely on

  • Real ~/.wrangler/config/default.toml is never written to by wrangler-accounts. If a user reports that it changed, something else touched it (e.g. a direct wrangler login outside wrangler-accounts).
  • Two wrangler-accounts --profile A and wrangler-accounts --profile B running in parallel never clobber each other on credentials OR account-id cache. Each gets its own mkdtemp shadow HOME, and each gets its own per-profile WRANGLER_CACHE_DIR (next to the profile's config.toml) so that wrangler's wrangler-account.json (which stores the selected Cloudflare account ID) is naturally isolated.
  • OAuth token refresh inside a profile is automatic. The shadow HOME contains a symlink from .wrangler/config/default.toml to the saved profile file, so Wrangler's in-place fs.writeFileSync during refreshToken() flows straight back to the profile.
  • wrangler-accounts <args> without a management subcommand forwards everything to wrangler verbatim, including --env, --dry-run, --json, and any wrangler-native flags. The only flags consumed by wrangler-accounts itself are the ones listed in "Paths and environment" below.

What is and isn't isolated

State Location Isolated?
OAuth credentials (config.toml) shadow $HOME/.wrangler/config/default.toml → symlink to per-profile file ✅ per profile
Account-id cache (wrangler-account.json) per-profile WRANGLER_CACHE_DIR (= <profilesDir>/<name>/cache/) ✅ per profile
Pages config cache (pages-config-cache.json) same as above ✅ per profile
Miniflare dev registry WRANGLER_REGISTRY_PATH = $realHome/.wrangler/registry ❌ shared on purpose (cross-profile worker discovery during local dev)
Wrangler debug logs WRANGLER_LOG_PATH = $realHome/.wrangler/logs ❌ shared (append-only, harmless)
Project-local state (./.wrangler/state/, ./node_modules/.cache/wrangler) inside the project directory ❌ shared at project level (per-project, but not per-profile)
cloudflared binary CLOUDFLARED_PATH or ~/.wrangler/cloudflared/ ❌ shared (binary, not account-scoped)
Shell history, npm cache, git config, ssh keys symlinked through to real $HOME ❌ shared by design (so exec subshells feel like a normal terminal)

If a user is hitting a "wrong account" symptom and the credentials look right, the most likely culprit is project-local state in ./.wrangler/state/ — clear that and re-run.

CI guidance

For CI and deploy pipelines you have two options:

Option 1 — plain wrangler with env vars (simplest for CI):

CLOUDFLARE_API_TOKEN=<token>
CLOUDFLARE_ACCOUNT_ID=<account-id>
wrangler deploy

Option 2 — wrangler-accounts with a token profile (useful when you want the same CLI both locally and in CI):

# Save once (locally or during CI bootstrap):
wrangler-accounts token-add work "$CF_TOKEN" "$CF_ACCOUNT_ID"
# Use the same commands locally and in CI:
wrangler-accounts --profile work deploy

Or use the anonymous pass-through (no profile needed if env vars are present):

CLOUDFLARE_API_TOKEN="$CF_TOKEN" CLOUDFLARE_ACCOUNT_ID="$CF_ACCOUNT_ID" wrangler-accounts deploy

wrangler-accounts is primarily a local developer convenience for juggling OAuth sessions on your workstation, but since 1.6.0 it also supports API token profiles for teams that want a consistent CLI interface across local and CI environments.

Paths and environment

  • --profile <name> / -p <name> — profile for this invocation (v1.0: -p means --profile)
  • --profiles <path> — profiles directory (long form only since v1.0)
  • -c, --config <path> — Wrangler config path
  • WRANGLER_PROFILE — profile to use when no --profile flag is given
  • WRANGLER_CONFIG_PATH, WRANGLER_ACCOUNTS_DIR, XDG_CONFIG_HOME — path overrides

Output conventions

Use --json when another tool needs to parse results. All v1.0 commands that produce structured data support --json.

Naming rules

Profile names: letters, numbers, dot, underscore, dash only. Names matching management subcommand names (exec, default, whoami, gc, login, token-add, list, status, save, sync, sync-default, remove, use, sync-active) cannot be reached via positional shorthand — use --profile <name> for those.

Deprecated

  • wrangler-accounts use <name> — unsupported compatibility entry; exits with migration guidance. Use default <name> for persistence, --profile <name> for one-shot commands, or exec <name> for an interactive shell.
  • wrangler-accounts sync-active — deprecated alias for sync-default.
Install via CLI
npx skills add https://github.com/leeguooooo/wrangler-accounts --skill wrangler-accounts
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator