|
| 1 | +# v2.1.13-beta.2 |
| 2 | + |
| 3 | +Beta prerelease that fixes the cascade OAuth token invalidation from issue #495: |
| 4 | +the rotation gateway was invalidating healthy accounts one after another by |
| 5 | +presenting each account's OAuth token from the same IP in rapid succession, |
| 6 | +tripping OpenAI's anti-abuse detection. The 401 handler now detects explicit |
| 7 | +token-invalidation responses and stops rotating instead of cascading through |
| 8 | +every account. |
| 9 | + |
| 10 | +This is a **prerelease** and also carries the multi-workspace support from |
| 11 | +v2.1.13-beta.1 and the pinned-account 503 diagnostic from v2.1.13-beta.0. Stable |
| 12 | +`v2.1.13` will land once the issue #486 root cause is identified and patched. |
| 13 | + |
| 14 | +## Install |
| 15 | + |
| 16 | +```bash |
| 17 | +npm i -g codex-multi-auth@beta |
| 18 | +``` |
| 19 | + |
| 20 | +## Runtime rotation |
| 21 | + |
| 22 | +### Fixes |
| 23 | + |
| 24 | +- **Detect token invalidation in 401 responses.** The 401 handler now reads the |
| 25 | + response body and checks for explicit invalidation phrases (e.g. |
| 26 | + `invalidated oauth token`, `authentication token has been invalidated`), |
| 27 | + distinguishing them from generic expired-token 401s. |
| 28 | +- **Stop cascade rotation on invalidation.** When an invalidation is detected the |
| 29 | + 401 is returned directly to the client instead of rotating to the next account. |
| 30 | + Rotating was the root cause of #495 — presenting each account's OAuth token from |
| 31 | + the same IP in quick succession triggered OpenAI's anti-abuse detection and |
| 32 | + invalidated the tokens in sequence. Generic 401s (expired tokens, wrong |
| 33 | + credentials) continue to rotate as before. |
| 34 | +- **Apply a long cooldown on invalidation.** Invalidated accounts get a 5-minute |
| 35 | + cooldown (`CODEX_AUTH_TOKEN_INVALIDATION_COOLDOWN_MS` / |
| 36 | + `tokenInvalidationCooldownMs`) versus the generic 30-second auth-failure |
| 37 | + cooldown. The cooldown is monotonic — it only extends, never shortens — so a |
| 38 | + racing generic 401 cannot truncate an invalidation cooldown. |
| 39 | +- **Clear session affinity on invalidation.** Affinity for the affected session |
| 40 | + key is cleared so the next request can pick a healthy account. |
| 41 | +- **Consistent client error contract.** Both invalidation exit paths |
| 42 | + (refresh-failure and upstream-401) emit the same |
| 43 | + `{ error: { message, code: "token_invalidated" } }` body through a single |
| 44 | + shared builder, preserving the upstream human-readable message when present and |
| 45 | + falling back to a stable message for non-JSON bodies. |
| 46 | + |
| 47 | +### Features |
| 48 | + |
| 49 | +- **`minRotationIntervalMs` sticky window.** A configurable sliding anchor |
| 50 | + (`CODEX_AUTH_MIN_ROTATION_INTERVAL_MS` / `minRotationIntervalMs`, default 60s) |
| 51 | + boosts the last-served account so back-to-back requests are less likely to |
| 52 | + present a different OAuth token from the same IP within the window. The anchor |
| 53 | + refreshes on every successful serve, not only on account change. |
| 54 | + |
| 55 | +## Release Hygiene |
| 56 | + |
| 57 | +### Tests |
| 58 | + |
| 59 | +- Upstream-401 invalidation returns 401 to the client, applies the ~5-minute |
| 60 | + cooldown, clears affinity, and does not rotate; generic 401 still rotates |
| 61 | + (regression guard); empty and HTML bodies handled. |
| 62 | +- Refresh-endpoint invalidation returns `code: "token_invalidated"` and routes |
| 63 | + through the shared body builder. |
| 64 | +- Eight unit cases for the invalidation body builder cover every |
| 65 | + message-extraction branch (top-level message, nested `error.message`, |
| 66 | + top-level-wins priority, blank-to-nested fallback, non-JSON body, and the |
| 67 | + no-usable-message fallback). |
| 68 | +- `minRotationIntervalMs` sliding-anchor and sticky-window coverage; schema and |
| 69 | + config coverage for both new knobs (`min(0)`, allows-zero, rejects-string). |
| 70 | + |
| 71 | +## Known Gaps |
| 72 | + |
| 73 | +- The issue #486 503 root cause (doctor reports all green, runtime still returns |
| 74 | + 503) is **not** fixed by this prerelease; stable `v2.1.13` remains gated on it. |
| 75 | + |
| 76 | +## Refs |
| 77 | + |
| 78 | +- Issue #495 — `Rotation gateway triggers mass OAuth token invalidation across accounts` |
| 79 | +- PR #496 — `fix: detect token invalidation in 401 responses and stop cascade rotation` |
| 80 | +- PR #497 — `fix(runtime): emit consistent token_invalidated body on upstream-401 path` |
0 commit comments