diff --git a/src/oauth/login-flow-state.ts b/src/oauth/login-flow-state.ts index 3a52a6a7151..0de081f63e1 100644 --- a/src/oauth/login-flow-state.ts +++ b/src/oauth/login-flow-state.ts @@ -305,7 +305,11 @@ export function cancelLoginFlow(provider: string, flowId?: string): boolean { const active = loginAbort.get(flowKey); const existing = loginState.get(flowKey); if ((active?.provider ?? loginFlowProvider(flowKey)) !== provider) return false; - if (!active && (!existing || existing.done)) return false; + if (!active) { + if (!existing) return false; + dropLoginFlow(flowKey); + return true; + } abandonLoginFlow(flowKey, { error: "Login cancelled", errorCode: "no_pending_login" }); return true; } diff --git a/tests/oauth/oauth-cancel-clears-transient-error.test.ts b/tests/oauth/oauth-cancel-clears-transient-error.test.ts new file mode 100644 index 00000000000..a3306d777cf --- /dev/null +++ b/tests/oauth/oauth-cancel-clears-transient-error.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from "bun:test"; +import { cancelLoginFlow, getLoginStatus } from "../../src/oauth"; +import { loginState } from "../../src/oauth/login-flow-state"; + +describe("OAuth login cancellation and error lifecycle", () => { + test("cancelLoginFlow clears finalized transient error state so it stops masking status", () => { + const provider = "google-antigravity"; + // Simulate a failed/aborted login attempt that settled with an error + loginState.set(provider, { + done: true, + error: "OAuth authentication failed. Check the OpenCodex account status and retry.", + }); + + // Before cancel, getLoginStatus reflects the error + let status = getLoginStatus(provider); + expect(status.error).toBe("OAuth authentication failed. Check the OpenCodex account status and retry."); + expect(status.done).toBe(true); + + // Call cancelLoginFlow + const cancelled = cancelLoginFlow(provider); + expect(cancelled).toBe(true); + + // After cancel, the transient error must be cleared + status = getLoginStatus(provider); + expect(status.error).toBeUndefined(); + expect(status.done).toBe(false); + }); +});