fix(refresh): rotate refresh_token between retry attempts
Rotating-RT providers (xAI/grok-cli) issue a new refresh_token on every refresh; mutate credentials in-place so refreshWithRetry reuses the fresh RT instead of the already-consumed one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -330,7 +330,18 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred
|
||||
// Handle 401/403 - try token refresh (skip for noAuth providers)
|
||||
if (!executor.noAuth && (providerResponse.status === HTTP_STATUS.UNAUTHORIZED || providerResponse.status === HTTP_STATUS.FORBIDDEN)) {
|
||||
try {
|
||||
const newCredentials = await refreshWithRetry(() => executor.refreshCredentials(credentials, log), 3, log);
|
||||
// Mutate credentials after each successful refresh: rotating refresh_token
|
||||
// providers (xAI/grok-cli) issue a new RT on every refresh; without this,
|
||||
// refreshWithRetry's 2nd/3rd attempt reuses the already-consumed RT →
|
||||
// invalid_grant → auth_failed retryable=false.
|
||||
const newCredentials = await refreshWithRetry(async () => {
|
||||
const result = await executor.refreshCredentials(credentials, log);
|
||||
if (result?.refreshToken && result.refreshToken !== credentials.refreshToken) {
|
||||
if (result.accessToken) credentials.accessToken = result.accessToken;
|
||||
credentials.refreshToken = result.refreshToken;
|
||||
}
|
||||
return result;
|
||||
}, 3, log);
|
||||
if (newCredentials?.accessToken || newCredentials?.copilotToken) {
|
||||
if (log?.line) log.line(reqTag, "🔑", `TOKEN REFRESHED · ${provider}/${model}`);
|
||||
Object.assign(credentials, newCredentials);
|
||||
|
||||
Reference in New Issue
Block a user