diff --git a/src/agents/auth-profiles/oauth-manager.ts b/src/agents/auth-profiles/oauth-manager.ts index 9456709a582..5713f6c2e4e 100644 --- a/src/agents/auth-profiles/oauth-manager.ts +++ b/src/agents/auth-profiles/oauth-manager.ts @@ -334,6 +334,7 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { if ( mainCred?.type === "oauth" && mainCred.provider === params.credential.provider && + hasUsableOAuthCredential(mainCred) && Number.isFinite(mainCred.expires) && (!Number.isFinite(params.credential.expires) || mainCred.expires > params.credential.expires) && @@ -651,13 +652,18 @@ export function createOAuthManager(adapter: OAuthManagerAdapter) { credential: recovered, }; } - const retried = await refreshOAuthTokenWithLock({ - profileId: params.profileId, - provider: params.credential.provider, - agentDir: params.agentDir, - }); - if (retried) { - return retried; + try { + const retried = await refreshOAuthTokenWithLock({ + profileId: params.profileId, + provider: params.credential.provider, + agentDir: params.agentDir, + }); + if (retried) { + return retried; + } + } catch { + // Retry failed too; keep flowing through the main-store fallback + // and final wrapped error path below. } } if (params.agentDir) { diff --git a/src/agents/auth-profiles/oauth.fallback-to-main-agent.test.ts b/src/agents/auth-profiles/oauth.fallback-to-main-agent.test.ts index e93c566e3a5..3e4ba5e429d 100644 --- a/src/agents/auth-profiles/oauth.fallback-to-main-agent.test.ts +++ b/src/agents/auth-profiles/oauth.fallback-to-main-agent.test.ts @@ -332,4 +332,44 @@ describe("resolveApiKeyForProfile fallback to main agent", () => { /OAuth token refresh failed/, ); }); + + it("still falls back to main agent credentials when the refresh-token-reused retry throws", async () => { + const profileId = "anthropic:claude-cli"; + const now = Date.now(); + const expiredTime = now - 60 * 60 * 1000; + const freshTime = now + 60 * 60 * 1000; + + await writeAuthProfilesStore( + secondaryAgentDir, + createOauthStore({ + profileId, + access: "expired-access-token", + refresh: "expired-refresh-token", + expires: expiredTime, + }), + ); + + await writeAuthProfilesStore( + mainAgentDir, + createOauthStore({ + profileId, + access: "fresh-access-token", + refresh: "fresh-refresh-token", + expires: freshTime, + }), + ); + + getOAuthApiKeyMock + .mockImplementationOnce(async () => { + throw new Error("refresh_token_reused"); + }) + .mockImplementationOnce(async () => { + throw new Error("retry also failed"); + }); + + const result = await resolveFromSecondaryAgent(profileId); + + expect(result?.apiKey).toBe("fresh-access-token"); + expect(result?.provider).toBe("anthropic"); + }); });