Skip to content

Detect stale GitHub credentials and prompt re-authentication#8728

Merged
alexr00 merged 3 commits into
mainfrom
copilot/fix-bad-credentials-error
May 8, 2026
Merged

Detect stale GitHub credentials and prompt re-authentication#8728
alexr00 merged 3 commits into
mainfrom
copilot/fix-bad-credentials-error

Conversation

Copilot AI commented May 7, 2026

Copy link
Copy Markdown
Contributor

API calls intermittently fail with Bad credentials - https://docs.github.com/rest once the shared auth token expires/is revoked, and the extension keeps reusing the stale token instead of prompting re-auth (unlike e.g. the GitHub Actions extension). Users currently have to sign out, fully restart VS Code, and sign back in.

Changes

  • Centralized auth-error detection (loggingOctokit.ts): New isAuthError(e) recognizes 401 status, Bad credentials REST messages, and 401 Unauthorized GraphQL/network errors. RateLogger now takes an optional auth-error callback and invokes it from the existing logApiError path, which is already wired into LoggingOctokit.call, LoggingApolloClient.query, and LoggingApolloClient.mutate — so every REST and GraphQL call is covered without touching call sites.

  • Re-auth trigger (credentials.ts): New CredentialStore.handleAuthError(authProviderId) forces a new session for the affected provider only (via initialize(authProviderId, { forceNewSession: { detail: reason } })) with a localized prompt. It deliberately bypasses recreate()/doCreate() so that when both GitHub.com and Enterprise are configured, only the provider that returned the auth error is re-prompted. It:

    • Skips when the provider isn't currently considered authenticated (the regular sign-in flow handles that).
    • Dedupes concurrent failures via an in-flight promise map so a burst of failing requests produces a single prompt.
    • Applies a 60s cooldown after a recreate so stragglers issued with the old token don't immediately re-prompt.
    • Treats dotcom and Enterprise independently.
  • Wiring (createHub): Constructs RateLogger with a handler bound to the appropriate AuthProvider.

  • Tests: src/test/github/loggingOctokit.test.ts covers isAuthError shapes (REST 401, message-based, GraphQL networkError, unrelated errors) and verifies RateLogger.logApiError invokes — and tolerates exceptions from — the handler.

Sketch

// loggingOctokit.ts — single chokepoint for every API call
public logApiError(info, apiResult) {
    apiResult.catch(e => {
        this.telemetry.sendTelemetryErrorEvent('pr.apiCallFailed', ...);
        if (this.authErrorHandler && isAuthError(e)) {
            try { this.authErrorHandler(e); } catch { /* ignore */ }
        }
    });
}

// credentials.ts — deduped + cooldown'd, provider-scoped re-auth
public async handleAuthError(authProviderId: AuthProvider) {
    if (!this.isAuthenticated(authProviderId)) return { canceled: true };
    const inFlight = this._handlingAuthError.get(authProviderId);
    if (inFlight) return inFlight;
    if (withinCooldown(authProviderId)) return { canceled: true };
    // ... initialize(authProviderId, { forceNewSession: { detail: reason } }), record timestamp on completion
}

The previous ad-hoc handlers (categoryNode.ts "Bad credentials" branch, githubRepository.query 401 branch) remain as-is; this layer catches the cases they miss (REST calls extension-wide, GraphQL mutations, and any other call site).

Copilot AI linked an issue May 7, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Fix bad credentials error during GitHub authentication Detect stale GitHub credentials and prompt re-authentication May 7, 2026
Copilot AI requested a review from alexr00 May 7, 2026 09:22
Copilot finished work on behalf of alexr00 May 7, 2026 09:22

@alexr00 alexr00 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of a dirty layering change, but looks like it will do the trick without requiring some significant refactoring.

@alexr00 alexr00 marked this pull request as ready for review May 7, 2026 09:47
Copilot AI review requested due to automatic review settings May 7, 2026 09:47
@alexr00 alexr00 added this to the 1.121.0 milestone May 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds centralized detection of authentication-related API failures (REST + GraphQL) and wires a re-authentication trigger so the extension can proactively prompt users to sign in again when tokens are revoked/expired instead of repeatedly retrying with stale credentials.

Changes:

  • Add isAuthError(e) and extend RateLogger to optionally invoke an auth-error handler from the existing logApiError chokepoint.
  • Add CredentialStore.handleAuthError(...) with de-duping and cooldown behavior, and wire it into createHub via the RateLogger handler.
  • Add unit tests for isAuthError and RateLogger.logApiError auth-handler invocation behavior.
Show a summary per file
File Description
src/github/loggingOctokit.ts Adds auth-error detection and a callback hook from RateLogger.logApiError for all REST/GraphQL call paths.
src/github/credentials.ts Implements de-duped/cooldown re-auth prompting and wires it to API error handling.
src/test/github/loggingOctokit.test.ts Adds unit tests for auth-error detection and handler invocation/swallowing behavior.

Copilot's findings

Comments suppressed due to low confidence (2)

src/github/credentials.ts:614

  • The auth error handler passed to RateLogger starts an async flow via void this.handleAuthError(authProviderId), but any rejection from handleAuthError (e.g., if initialize/getSession throws) will become an unhandled promise rejection. This should be caught and logged/ignored here (or handleAuthError should internally swallow and return { canceled: true } on errors) to avoid destabilizing the extension host.
		const rateLogger = new RateLogger(this._telemetry, isEnterprise(authProviderId), (_e) => {
			void this.handleAuthError(authProviderId);
		});

src/github/credentials.ts:330

  • _lastAuthErrorHandledAt is updated in the finally block regardless of whether recreate(reason) succeeded or the user canceled/it threw. This can start the cooldown even though no new valid session was established, potentially suppressing re-prompting for 60s while requests continue to fail. Consider only setting the cooldown timestamp after a successful re-auth (and possibly not when canceled: true).
		const promise = (async () => {
			try {
				return await this.recreate(reason);
			} finally {
				this._handlingAuthError.delete(authProviderId);
				this._lastAuthErrorHandledAt.set(authProviderId, Date.now());
			}
  • Files reviewed: 3/3 changed files
  • Comments generated: 1

Comment thread src/github/credentials.ts Outdated
const reason = vscode.l10n.t('Your GitHub{0} authentication session is no longer valid. Please sign in again.', getGitHubSuffix(authProviderId));
const promise = (async () => {
try {
return await this.recreate(reason);
@alexr00

alexr00 commented May 7, 2026

Copy link
Copy Markdown
Member

@copilot, address the unresolved PR comment.

Copilot AI commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

@copilot, address the unresolved PR comment.

Addressed in 83d068ehandleAuthError now calls initialize(authProviderId, { forceNewSession }) directly, so only the affected provider is re-prompted instead of going through recreate()/doCreate() which re-auths both dotcom and Enterprise.

Copilot finished work on behalf of alexr00 May 7, 2026 10:03
Copilot AI requested a review from alexr00 May 7, 2026 10:03
@alexr00 alexr00 merged commit a0d196e into main May 8, 2026
6 checks passed
@alexr00 alexr00 deleted the copilot/fix-bad-credentials-error branch May 8, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bad Credentials

4 participants