google-gemini/gemini-cli · error · FatalAuthenticationError
Failed to open browser: ${getErrorMessage(err)}
Error message
Failed to open browser: ${getErrorMessage(err)} What it means
Thrown as a FatalAuthenticationError when the open() library fails to launch a browser to display the OAuth authentication URL. The code calls open(webLogin.authUrl) to spawn the system's default browser (xdg-open on Linux, open on macOS, start on Windows). If the spawn fails — typically because the command doesn't exist — the error is caught, a user-feedback event is emitted, and a fatal error is thrown. The error message suggests NO_BROWSER=true as a workaround.
Source
Thrown at packages/core/src/code_assist/oauth2.ts:356
// Without this, if `open` fails to spawn a process (e.g., `xdg-open` is not found
// in a minimal Docker container), it will emit an unhandled 'error' event,
// causing the entire Node.js process to crash.
childProcess.on('error', (error) => {
coreEvents.emit(CoreEvent.UserFeedback, {
severity: 'error',
message:
`Failed to open browser with error: ${getErrorMessage(error)}\n` +
`Please try running again with NO_BROWSER=true set.`,
});
});
} catch (err) {
coreEvents.emit(CoreEvent.UserFeedback, {
severity: 'error',
message:
`Failed to open browser with error: ${getErrorMessage(err)}\n` +
`Please try running again with NO_BROWSER=true set.`,
});
throw new FatalAuthenticationError(
`Failed to open browser: ${getErrorMessage(err)}`,
);
}
coreEvents.emit(CoreEvent.UserFeedback, {
severity: 'info',
message: 'Waiting for authentication...\n',
});
// Add timeout to prevent infinite waiting when browser tab gets stuck
const authTimeout = 5 * 60 * 1000; // 5 minutes timeout
let timeoutId: NodeJS.Timeout | undefined;
const timeoutPromise = new Promise<never>((_, reject) => {
timeoutId = setTimeout(() => {
reject(
new FatalAuthenticationError(
'Authentication timed out after 5 minutes. The browser tab may have gotten stuck in a loading state. ' +
'Please try again or use NO_BROWSER=true for manual authentication.',
),View on GitHub (pinned to 5024443c72)
Solutions
- Set NO_BROWSER=true and run in an interactive terminal to use the manual user-code flow instead.
- Install xdg-utils (provides xdg-open) on Linux: apt-get install xdg-utils.
- In containers, prefer GEMINI_API_KEY or ADC over browser-based OAuth.
- If a browser is available but not detected, set the BROWSER environment variable to the browser binary path.
Defensive patterns
Strategy: validation
Validate before calling
// Check for a browser handler before attempting to open
import { existsSync } from 'node:fs';
function hasBrowserHandler(): boolean {
if (process.platform === 'linux') {
return existsSync('/usr/bin/xdg-open');
}
return true; // macOS/Windows have built-in handlers
}
if (!hasBrowserHandler()) {
process.env['NO_BROWSER'] = 'true';
console.warn('No browser handler found. Using manual auth flow.');
} Try / catch
try {
client = await getOauthClient(authType, config);
} catch (e) {
if (e instanceof FatalAuthenticationError && e.message.startsWith('Failed to open browser')) {
// Fall back to manual code entry
process.env['NO_BROWSER'] = 'true';
client = await getOauthClient(authType, config);
} else throw e;
} Prevention
- Install xdg-utils in Linux containers for browser-based OAuth.
- Set NO_BROWSER=true proactively in headless environments.
- Use GEMINI_API_KEY or ADC when no browser is available.
- Set the BROWSER env var if the default handler is misconfigured.
When it happens
Trigger: In the interactive OAuth branch, open(webLogin.authUrl) throws because the OS has no default browser handler. The open library spawns a child process (e.g., xdg-open); if the binary is missing or errors, the promise rejects.
Common situations: Minimal Docker container or server without a browser or xdg-open installed; DISPLAY environment variable unset on Linux; a headless environment where no GUI browser exists; the default browser binary path is misconfigured; SELinux/AppArmor blocking process spawn.
Related errors
- Manual authorization is required but the current session is
- Failed to authenticate with user code.
- Authentication cancelled by user.
- ${originalMessage}. The initial COMPUTE_ADC attempt also fai
- Failed to load OAuth credentials
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/9cfe5a54afe541c9.
Report an issue: GitHub.