google-gemini/gemini-cli · warning · FatalCancellationError

Authentication cancelled by user.

Error message

Authentication cancelled by user.

What it means

Before opening the browser for the OAuth flow, getConsentForOauth prompts the user; if they decline, the provider throws FatalCancellationError('Authentication cancelled by user.'). This is an intentional user action, not a system failure. FatalCancellationError signals that retrying the same flow without a different user decision will not help, so callers should treat it as terminal for this attempt.

Source

Thrown at packages/core/src/agents/auth-provider/oauth2-provider.ts:249

    };

    const pkceParams = generatePKCEParams();
    const preferredPort = getPortFromUrl(flowConfig.redirectUri);
    const callbackServer = startCallbackServer(pkceParams.state, preferredPort);
    const redirectPort = await callbackServer.port;

    const authUrl = buildAuthorizationUrl(
      flowConfig,
      pkceParams,
      redirectPort,
      /* resource= */ undefined, // No MCP resource parameter for A2A.
    );

    const consent = await getConsentForOauth(
      `Authentication required for A2A agent: '${this.agentName}'.`,
    );
    if (!consent) {
      throw new FatalCancellationError('Authentication cancelled by user.');
    }

    coreEvents.emitFeedback(
      'info',
      `→ Opening your browser for OAuth sign-in...

` +
        `If the browser does not open, copy and paste this URL into your browser:
` +
        `${authUrl}

` +
        `💡 TIP: Triple-click to select the entire URL, then copy and paste it into your browser.
` +
        `⚠️  Make sure to copy the COMPLETE URL - it may wrap across multiple lines.`,
    );

    try {

View on GitHub (pinned to 5024443c72)

Solutions

  1. Catch FatalCancellationError specifically and surface a clean 'cancelled' message rather than an error.
  2. Re-invoke the flow only when the user explicitly requests it again.
  3. For non-interactive contexts, pre-seed a stored token to skip the consent prompt.
  4. Do not auto-retry on this error; it is not transient.

Example fix

// before
const token = await provider.getToken();

// after
try {
  const token = await provider.getToken();
} catch (e) {
  if (e instanceof FatalCancellationError) {
    console.log('User cancelled authentication.');
    return;
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Type guard

function isAuthCancelled(e: unknown): boolean {
  return e instanceof FatalCancellationError
    && /cancelled by user/i.test(e.message);
}

Try / catch

try {
  return await provider.getToken();
} catch (e) {
  if (e instanceof FatalCancellationError) {
    // user declined - do not retry automatically
    return { cancelled: true } as const;
  }
  throw e;
}

Prevention

When it happens

Trigger: The user answered 'no' to the consent prompt; the prompt timed out and defaulted to decline; a non-interactive context where consent could not be obtained.

Common situations: User does not want to grant access to the agent; accidental decline; automation/sandbox where the prompt cannot render; the user cancelled to switch agents.

Understand the failure class

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/f8b6a653103ee075. Report an issue: GitHub.