v2.8.6
August 16, 2026 · View on GitHub
Bug fixes for the commands that tell you whether an account is usable, and for the error you get when a pinned account isn't. Nothing to migrate — install and go.
npm i -g codex-multi-auth
What was wrong
Two surfaces were lying to you, in opposite directions.
forecast, best, and report checked the wrong rate limits. When you asked about a specific model, they looked up rate-limit records under a hardwired codex family instead of the family that model actually belongs to. On 2026-08-15 an account with a live gpt-5.2 limit had every session failing — 148 requests, 9 successes — while forecast --model gpt-5.6-sol reported both accounts ready with nothing to say. The one command that should have explained the outage called the account healthy.
The same lookup was wrong in the other direction too. It counted a rate limit belonging to any model in the family, not the one you asked about, so a limit on a model you weren't using could report a delay the proxy would never impose. And when two limits overlapped it reported the earlier one, telling you to come back before the account was actually usable.
The pinned-account 503 gave advice that couldn't work. When a pinned account is unavailable the proxy returns codex_pinned_account_unavailable, and it always said to run codex-multi-auth unpin. That is right for a pin you set with switch. It is wrong for a pin set per-invocation by --account or CODEX_MULTI_AUTH_FORCE_ACCOUNT — unpin clears nothing there, because that pin belongs to the launching process. Those launcher-managed sessions are where this message shows up most. The response also never told you when the account would come back, even when the store knew.
What's fixed
Availability now matches what the proxy actually does.
forecast --model X,best --model X, andreport --model Xcheck the rate-limit records for that model's family and for that specific model — the same two keys account selection consults.- A limit on a different model in the same family no longer reports a delay for a model that isn't limited.
- When both a family-wide and a model-specific limit are active, the wait reflects the later one, so coming back at the stated time actually works.
- A recorded
rate-limitedstate backed by a live limit for the family you asked about is no longer discarded as stale.
Without --model, nothing changes. status and fix have no model to reason about, so they keep counting every limit in the family, exactly as before.
The pinned 503 now tells the truth.
- A forced pin is told to relaunch, not to run
unpin. - The response carries when the account recovers, taking the latest of its rate limit, its cooldown, and its circuit breaker's next attempt — whichever actually holds it back.
- A permanent blocker — disabled account, no enabled workspace, invalidated token, policy block — reports no recovery time at all, rather than a deadline that expires into another 503. This now holds even when the request itself is what disabled the account.
- A corrupt timestamp in stored account state no longer turns the whole response into a generic 500 that identifies nothing.
New fields on codex_pinned_account_unavailable
If you handle this error programmatically, three fields were added. The existing fields are unchanged.
| Field | Type | What it means |
|---|---|---|
pin_source | "forced", "manual", or null | manual came from switch and clears with unpin. forced came from the launcher and does not |
reset_at | ISO timestamp or null | When the account becomes selectable again. null when nothing bounds it, including under a permanent blocker |
retry_after_ms | number or null | The same moment in milliseconds |
One caveat worth knowing if you share a backoff routine across both 503 codes: retry_after_ms here is the latest bound for one pinned account, while codex_runtime_rotation_pool_exhausted reports the earliest recovery across the whole pool. It is advisory and is not sent as a Retry-After header. Full contract in reference/error-contracts.md.
Reported, diagnosed, and fixed by @possibilities.