From a0d547034cbef15c3bc418950183d1e62ceaba83 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 30 Jan 2026 02:53:34 +0000 Subject: [PATCH] fix: await saveCredentialsForProfile inside .then() callback to prevent race condition Move saveCredentialsForProfile calls inside the .then() callbacks and make them async/await to ensure credentials are saved as part of the promise chain. This prevents race conditions when multiple concurrent requests share the same refresh promise. --- src/integrations/openai-codex/oauth.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/integrations/openai-codex/oauth.ts b/src/integrations/openai-codex/oauth.ts index 7ee81df5f3..061815fdb2 100644 --- a/src/integrations/openai-codex/oauth.ts +++ b/src/integrations/openai-codex/oauth.ts @@ -490,13 +490,17 @@ export class OpenAiCodexOAuthManager { `[openai-codex-oauth] Access token expired for profile ${profileId || "global"} (expires=${credentials.expires}). Refreshing...`, ) const prevRefreshToken = credentials.refresh_token - refreshPromise = refreshAccessToken(credentials).then((newCreds) => { + refreshPromise = refreshAccessToken(credentials).then(async (newCreds) => { const rotated = newCreds.refresh_token !== prevRefreshToken this.log( `[openai-codex-oauth] Refresh response received for profile ${profileId || "global"} (expires_in≈${Math.round( (newCreds.expires - Date.now()) / 1000, )}s, refresh_token_rotated=${rotated})`, ) + await this.saveCredentialsForProfile(newCreds, profileId) + this.log( + `[openai-codex-oauth] Token persisted for profile ${profileId || "global"} (expires=${newCreds.expires})`, + ) return newCreds }) this.refreshPromises.set(cacheKey, refreshPromise) @@ -504,10 +508,6 @@ export class OpenAiCodexOAuthManager { const newCredentials = await refreshPromise this.refreshPromises.delete(cacheKey) - await this.saveCredentialsForProfile(newCredentials, profileId) - this.log( - `[openai-codex-oauth] Token persisted for profile ${profileId || "global"} (expires=${newCredentials.expires})`, - ) credentials = newCredentials } catch (error) { this.refreshPromises.delete(cacheKey) @@ -553,13 +553,17 @@ export class OpenAiCodexOAuthManager { this.log( `[openai-codex-oauth] Forcing token refresh for profile ${profileId || "global"} (expires=${credentials.expires})...`, ) - refreshPromise = refreshAccessToken(credentials).then((newCreds) => { + refreshPromise = refreshAccessToken(credentials).then(async (newCreds) => { const rotated = newCreds.refresh_token !== prevRefreshToken this.log( `[openai-codex-oauth] Forced refresh response received for profile ${profileId || "global"} (expires_in≈${Math.round( (newCreds.expires - Date.now()) / 1000, )}s, refresh_token_rotated=${rotated})`, ) + await this.saveCredentialsForProfile(newCreds, profileId) + this.log( + `[openai-codex-oauth] Forced token persisted for profile ${profileId || "global"} (expires=${newCreds.expires})`, + ) return newCreds }) this.refreshPromises.set(cacheKey, refreshPromise) @@ -567,10 +571,6 @@ export class OpenAiCodexOAuthManager { const newCredentials = await refreshPromise this.refreshPromises.delete(cacheKey) - await this.saveCredentialsForProfile(newCredentials, profileId) - this.log( - `[openai-codex-oauth] Forced token persisted for profile ${profileId || "global"} (expires=${newCredentials.expires})`, - ) return newCredentials.access_token } catch (error) { this.refreshPromises.delete(cacheKey)