{"record":{"id":"9cfe5a54afe541c9","repo":"google-gemini/gemini-cli","slug":"failed-to-open-browser-geterrormessage-err","errorCode":null,"errorMessage":"Failed to open browser: ${getErrorMessage(err)}","messagePattern":"Failed to open browser: (.+?)","errorType":"exception","errorClass":"FatalAuthenticationError","httpStatus":null,"severity":"error","filePath":"packages/core/src/code_assist/oauth2.ts","lineNumber":356,"sourceCode":"      // Without this, if `open` fails to spawn a process (e.g., `xdg-open` is not found\n      // in a minimal Docker container), it will emit an unhandled 'error' event,\n      // causing the entire Node.js process to crash.\n      childProcess.on('error', (error) => {\n        coreEvents.emit(CoreEvent.UserFeedback, {\n          severity: 'error',\n          message:\n            `Failed to open browser with error: ${getErrorMessage(error)}\\n` +\n            `Please try running again with NO_BROWSER=true set.`,\n        });\n      });\n    } catch (err) {\n      coreEvents.emit(CoreEvent.UserFeedback, {\n        severity: 'error',\n        message:\n          `Failed to open browser with error: ${getErrorMessage(err)}\\n` +\n          `Please try running again with NO_BROWSER=true set.`,\n      });\n      throw new FatalAuthenticationError(\n        `Failed to open browser: ${getErrorMessage(err)}`,\n      );\n    }\n    coreEvents.emit(CoreEvent.UserFeedback, {\n      severity: 'info',\n      message: 'Waiting for authentication...\\n',\n    });\n\n    // Add timeout to prevent infinite waiting when browser tab gets stuck\n    const authTimeout = 5 * 60 * 1000; // 5 minutes timeout\n    let timeoutId: NodeJS.Timeout | undefined;\n    const timeoutPromise = new Promise<never>((_, reject) => {\n      timeoutId = setTimeout(() => {\n        reject(\n          new FatalAuthenticationError(\n            'Authentication timed out after 5 minutes. The browser tab may have gotten stuck in a loading state. ' +\n              'Please try again or use NO_BROWSER=true for manual authentication.',\n          ),","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/core/src/code_assist/oauth2.ts#L338-L374","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check for a browser handler before attempting to open\nimport { existsSync } from 'node:fs';\n\nfunction hasBrowserHandler(): boolean {\n  if (process.platform === 'linux') {\n    return existsSync('/usr/bin/xdg-open');\n  }\n  return true; // macOS/Windows have built-in handlers\n}\n\nif (!hasBrowserHandler()) {\n  process.env['NO_BROWSER'] = 'true';\n  console.warn('No browser handler found. Using manual auth flow.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  client = await getOauthClient(authType, config);\n} catch (e) {\n  if (e instanceof FatalAuthenticationError && e.message.startsWith('Failed to open browser')) {\n    // Fall back to manual code entry\n    process.env['NO_BROWSER'] = 'true';\n    client = await getOauthClient(authType, config);\n  } else throw e;\n}","preventionTips":["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."],"tags":["oauth","authentication","browser","environment","fatal"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}