fix(resilience): terminal-skip spares the recoverable GitHub Copilot no_refresh_token state (#8389)

Cause: #8182's terminal-connection guard in checkConnection() returns
early for any testStatus in {credits_exhausted, banned, expired} to
stop the sweep from wasting CPU/network probing connections that can
never self-heal. But testStatus="expired" + errorCode="no_refresh_token"
is exactly the state the pre-existing GitHub Copilot self-heal targets
(isGitHubAccessTokenOnlyConnection + canClearGitHubNoRefreshTokenState,
~line 83-97 / 413-492): a Copilot connection with no OAuth refresh
token but a still-valid copilotToken, which the sweep is supposed to
flip back to "active". With the new guard placed ahead of that block
unconditionally, the self-heal became unreachable for exactly the
state it exists to clear.

Impact: healthy GitHub Copilot connections that once lost their OAuth
refresh token got stuck at testStatus="expired" in the dashboard
forever, even though their Copilot sub-token kept working and the
sweep would have cleared the stale status back to "active" every
cycle before #8182.

Fix: carve out the exact recoverable shape from the terminal-skip
guard — testStatus==="expired" && errorCode==="no_refresh_token" &&
isGitHubAccessTokenOnlyConnection(conn) — so the guard still skips
every other terminal case (credits_exhausted, banned, and "expired"
for any other reason) untouched, matching #8182's original intent.

Validation: tests/unit/token-health-no-refresh-token-expired-5326.test.ts
was red (1 fail / 4 pass) before the fix — "checkConnection clears
stale no_refresh_token state for usable GitHub Copilot connections"
asserted testStatus flips back to "active" but got "expired". Green
after the fix (6/6, including a new boundary test proving a GitHub
Copilot connection expired for any OTHER reason, e.g. errorCode
"invalid_grant", is still skipped untouched). Also reran the adjacent
checkConnection/tokenHealthCheck suites (token-health-check.test.ts,
token-health-check-circuit-breaker.test.ts,
apikey-connection-health-check.test.ts, token-health-check-sweep.test.ts,
token-health-check-tickms-defined.test.ts, tokenHealthCheck-batchSize.test.ts,
codex-oauth-refresh-persist-6352.test.ts, oauth-providers-error-handling.test.ts)
— all green, confirming #8182's terminal-skip behavior is otherwise
unchanged. npm run typecheck:core clean.

Refs #8182
Refs #8286
Refs #5326
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-24 09:56:58 -03:00
committed by GitHub
parent d095555d68
commit f0096f0224
2 changed files with 57 additions and 1 deletions

View File

@@ -405,8 +405,23 @@ export async function checkConnection(conn) {
// CPU and network on every sweep cycle. Mirrors isTerminalConnectionStatus
// in src/sse/services/auth.ts and TERMINAL_CONNECTION_STATUSES in
// src/lib/quota/connectionRecovery.ts.
//
// #5326 exception: a GitHub Copilot access-token-only connection parked in
// "expired" with errorCode "no_refresh_token" is NOT actually terminal — it's
// the exact target of the self-heal below (canClearGitHubNoRefreshTokenState),
// which clears that stale status back to "active" once the Copilot sub-token
// proves usable. Treating it as terminal here made that self-heal unreachable,
// leaving healthy Copilot connections stuck at "expired" forever.
const isRecoverableGithubCopilotNoRefresh =
conn.testStatus === "expired" &&
conn.errorCode === "no_refresh_token" &&
isGitHubAccessTokenOnlyConnection(conn);
const terminalStatuses = new Set(["credits_exhausted", "banned", "expired"]);
if (typeof conn.testStatus === "string" && terminalStatuses.has(conn.testStatus.toLowerCase())) {
if (
typeof conn.testStatus === "string" &&
terminalStatuses.has(conn.testStatus.toLowerCase()) &&
!isRecoverableGithubCopilotNoRefresh
) {
return;
}

View File

@@ -174,3 +174,44 @@ test("checkConnection clears stale no_refresh_token state for usable GitHub Copi
assert.equal(updated?.lastError ?? null, null);
assert.ok(updated?.lastHealthCheckAt);
});
// Boundary regression for #8182 vs #5326: the terminal-skip guard added by #8182
// must keep skipping a GitHub Copilot connection that is "expired" for a DIFFERENT
// reason than the recoverable no_refresh_token self-heal above (e.g. a manually
// banned/invalidated account). Only the EXACT no_refresh_token shape is exempted
// from the terminal skip — this proves the #5326 fix did not reopen #8182's
// wasted-probe fix for every "expired" GitHub connection.
test("checkConnection still skips a GitHub Copilot connection expired for a non-no_refresh_token reason (#8182 boundary)", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "github",
authType: "oauth",
name: "GitHub Genuinely Expired Account",
accessToken: "github-access-token",
refreshToken: null,
providerSpecificData: {
copilotToken: "copilot-token",
copilotTokenExpiresAt: Math.floor((Date.now() + 60 * 60 * 1000) / 1000),
},
testStatus: "expired",
errorCode: "invalid_grant",
lastError: "Manually invalidated by operator.",
isActive: true,
});
await tokenHealthCheck.checkConnection(connection);
const updated = await providersDb.getProviderConnectionById(getCreatedConnectionId(connection));
assert.equal(
updated?.testStatus,
"expired",
"terminal-skip must still apply outside the exact no_refresh_token shape"
);
assert.equal(updated?.errorCode, "invalid_grant");
assert.equal(
updated?.lastHealthCheckAt ?? null,
null,
"checkConnection must return early without touching the row at all"
);
});