unslothai/unsloth · warning · CodexAuthError
ChatGPT credential update is busy. Please retry.
Error message
ChatGPT credential update is busy. Please retry.
What it means
CodexAuthError raised by provider_oauth_write_guard when acquiring the cross-worker file lock for a provider's OAuth credentials times out (default 30s, acquired off the event loop via asyncio.to_thread). The guard serializes ChatGPT credential refresh and deletion across Studio workers; a concurrent long refresh holds the lock, and instead of blocking indefinitely the waiter reports 'busy, please retry'.
Source
Thrown at studio/backend/core/inference/openai_codex_auth.py:116
def _provider_file_lock(provider_id: str) -> FileLock:
lock_name = hashlib.sha256(provider_id.encode()).hexdigest()[:24]
return FileLock(
str(studio_db_path().parent / f".openai-codex-refresh-{lock_name}.lock"),
timeout = 30,
thread_local = False,
)
@asynccontextmanager
async def provider_oauth_write_guard(provider_id: str):
"""Serialize refresh and deletion across Studio workers without blocking the event loop."""
file_lock = _provider_file_lock(provider_id)
try:
await asyncio.to_thread(file_lock.acquire)
except FileLockTimeout as exc:
raise CodexAuthError("ChatGPT credential update is busy. Please retry.") from exc
try:
yield
finally:
await asyncio.to_thread(file_lock.release)
def _flow_is_stale(flow: OAuthFlow, now: float) -> bool:
if flow.status == "pending":
return now >= flow.expires_at
return now >= min(flow.expires_at, flow.created_at + _FLOW_TERMINAL_RETENTION_SECONDS)
async def _prune_flows() -> None:
now = time.time()
for flow_id, flow in list(_flows.items()):
if _flow_is_stale(flow, now):
await cancel_flow(flow_id)
View on GitHub (pinned to 203007d190)
Solutions
- Retry the operation after a short backoff — once the in-flight refresh finishes, the lock frees and the cached token is likely already fresh.
- Debounce refresh at the call site: check token expiry with margin and only one caller refreshes (single-flight), so workers do not pile on the lock.
- If it recurs constantly, investigate why the lock holder exceeds 30s (network to the token endpoint) and raise the timeout or fix connectivity.
Example fix
# before
async with provider_oauth_write_guard(provider_id):
await refresh_credentials(provider_id) # raises immediately under contention
# after
for attempt in range(3):
try:
async with provider_oauth_write_guard(provider_id):
await refresh_credentials(provider_id)
break
except CodexAuthError:
await asyncio.sleep(2 * (attempt + 1)) Defensive patterns
Strategy: retry
Try / catch
for attempt in range(3):
try:
async with provider_oauth_write_guard(provider_id):
return await refresh(provider_id)
except CodexAuthError as e:
if 'busy' in str(e) and attempt < 2:
await asyncio.sleep(1.5 * (attempt + 1))
continue
raise Prevention
- Single-flight refreshes: only one caller refreshes per provider; others await the result, so the lock is rarely contended.
- Refresh tokens proactively with a margin (e.g. 60s before expiry) instead of on the request path.
- Treat 'busy' as transient — surface a retryable error to clients, not a hard failure.
When it happens
Trigger: Two or more workers/requests simultaneously refresh or delete the same provider's ChatGPT credentials; the first holds the lock longer than the 30s timeout (slow token endpoint), and the second's file_lock.acquire raises FileLockTimeout.
Common situations: Burst of requests right after access-token expiry, all triggering refresh; a hung/slow OpenAI auth endpoint during an outage; a previous worker crashed mid-refresh leaving contention behind (the lock eventually times out).
Related errors
- A transformers installation is replacing the latest sidecar;
- The diffusion engine changed while this load was starting. R
- ChatGPT returned an invalid access token.
- The ChatGPT account identifier was missing.
- ChatGPT returned an invalid token response.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/ea508df863d69f1c.
Report an issue: GitHub.