openclaw - 💡(How to fix) Fix Google Gemini CLI OAuth hangs indefinitely when browser launch silently fails [1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#70964Fetched 2026-04-24 10:37:21
View on GitHub
Comments
1
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
commented ×1

Running openclaw models auth login --provider google-gemini-cli hangs indefinitely on my machine because ctx.openUrl(authUrl) in dist/extensions/google/oauth.js silently fails to launch the browser (no tab opens, no exception thrown). The existing fallback code only runs on catch, so when launch fails without throwing, the authorization URL is never shown to the user and the flow deadlocks on the local callback server until timeout.

Error Message

Running openclaw models auth login --provider google-gemini-cli hangs indefinitely on my machine because ctx.openUrl(authUrl) in dist/extensions/google/oauth.js silently fails to launch the browser (no tab opens, no exception thrown). The existing fallback code only runs on catch, so when launch fails without throwing, the authorization URL is never shown to the user and the flow deadlocks on the local callback server until timeout. 4. Observe: Waiting for OAuth callback on http://localhost:8085/oauth2callback, no browser tab opens, no URL printed, eventual timeout with Error: OAuth callback timeout. The intent is clear: if openUrl fails, print the URL so the user can open it themselves. But ctx.openUrl (presumably wrapping open from npm, or similar) returns a resolved promise even when the underlying spawn doesn't actually launch a browser — a known limitation of that pattern on Windows. The result: no exception, catch never fires, URL never shown, user stuck.

Root Cause

In dist/extensions/google/oauth.js, the non-manual flow has this fallback structure:

try {
    await ctx.openUrl(authUrl);
} catch {
    ctx.log(`\nOpen this URL in your browser:\n\n${authUrl}\n`);
}

The intent is clear: if openUrl fails, print the URL so the user can open it themselves. But ctx.openUrl (presumably wrapping open from npm, or similar) returns a resolved promise even when the underlying spawn doesn't actually launch a browser — a known limitation of that pattern on Windows. The result: no exception, catch never fires, URL never shown, user stuck.

Fix Action

Fix / Workaround

Verification

Applied this patch locally. openclaw models auth login --provider google-gemini-cli now prints the auth URL immediately, I paste it into a browser, complete the flow, and OpenClaw's callback server receives the code as expected. Output ends with Gemini CLI OAuth complete. openclaw doctor confirms the profile transitions from expired (0m) + OAuth refresh errors to expiring (40m) with no refresh errors.

Code Example

openclaw models auth login --provider google-gemini-cli

---

try {
    await ctx.openUrl(authUrl);
} catch {
    ctx.log(`\nOpen this URL in your browser:\n\n${authUrl}\n`);
}

---

ctx.log(`\nOpen this URL in your browser:\n\n${authUrl}\n`);
try {
    await ctx.openUrl(authUrl);
} catch {
    // URL already printed; nothing else to do.
}
RAW_BUFFERClick to expand / collapse

Environment

  • OpenClaw: 2026.4.22 (00bd2cf)
  • OS: Windows 11, PowerShell 5.1 (non-elevated user session)
  • Node.js: v24.6.0
  • npm: 11.11.1
  • Install: global (npm install -g openclaw)
  • Default browser: set and functional (Start-Process "https://..." launches it reliably)
  • BROWSER env var: unset
  • Port 8085: free

Summary

Running openclaw models auth login --provider google-gemini-cli hangs indefinitely on my machine because ctx.openUrl(authUrl) in dist/extensions/google/oauth.js silently fails to launch the browser (no tab opens, no exception thrown). The existing fallback code only runs on catch, so when launch fails without throwing, the authorization URL is never shown to the user and the flow deadlocks on the local callback server until timeout.

Reproduction

  1. Windows, default browser set, no BROWSER env var.
  2. Run from a non-admin PowerShell prompt:
    openclaw models auth login --provider google-gemini-cli
  3. Confirm "Yes" when asked.
  4. Observe: Waiting for OAuth callback on http://localhost:8085/oauth2callback, no browser tab opens, no URL printed, eventual timeout with Error: OAuth callback timeout.

Confirmed that the underlying callback server is healthy — navigating manually to http://localhost:8085/oauth2callback returns OpenClaw's own "Missing OAuth code or state" page from the expected handler, confirming the server is bound and listening. So the bug is strictly on the browser-launch side.

Root cause

In dist/extensions/google/oauth.js, the non-manual flow has this fallback structure:

try {
    await ctx.openUrl(authUrl);
} catch {
    ctx.log(`\nOpen this URL in your browser:\n\n${authUrl}\n`);
}

The intent is clear: if openUrl fails, print the URL so the user can open it themselves. But ctx.openUrl (presumably wrapping open from npm, or similar) returns a resolved promise even when the underlying spawn doesn't actually launch a browser — a known limitation of that pattern on Windows. The result: no exception, catch never fires, URL never shown, user stuck.

Proposed fix

Print the URL unconditionally before attempting browser launch, so a silent launch failure is never fatal:

ctx.log(`\nOpen this URL in your browser:\n\n${authUrl}\n`);
try {
    await ctx.openUrl(authUrl);
} catch {
    // URL already printed; nothing else to do.
}

If the browser launch works, the user ignores the printed URL (no harm). If it silently fails, the URL is right there. This matches the defensive pattern many CLIs use (gcloud, gh, etc.) and eliminates a whole class of "OAuth just hangs on Windows" bug reports.

Verification

Applied this patch locally. openclaw models auth login --provider google-gemini-cli now prints the auth URL immediately, I paste it into a browser, complete the flow, and OpenClaw's callback server receives the code as expected. Output ends with Gemini CLI OAuth complete. openclaw doctor confirms the profile transitions from expired (0m) + OAuth refresh errors to expiring (40m) with no refresh errors.

Additional notes

  • The identical pattern likely exists in other OAuth providers under dist/extensions/*/oauth.js; a grep for ctx.openUrl wrapped in try { ... } catch { ctx.log(...) } would catch them.
  • A related improvement: consider always printing the URL at debug or higher log level even when browser launch succeeds, so users on misbehaving setups have a record they can copy.
  • The underlying silent failure of ctx.openUrl on some Windows configurations is a separate, harder bug (probably in the npm open package or how it's invoked with Node v24). The fix above sidesteps it entirely without needing to diagnose it.

extent analysis

TL;DR

Print the authorization URL unconditionally before attempting to launch the browser to prevent silent launch failures from causing the OAuth flow to hang.

Guidance

  • Verify that the proposed fix works by applying the patch locally and testing the openclaw models auth login --provider google-gemini-cli command.
  • Check for similar patterns in other OAuth providers under dist/extensions/*/oauth.js to ensure the fix is applied consistently.
  • Consider always printing the URL at debug or higher log level, even when browser launch succeeds, to provide a record for users on misbehaving setups.
  • Note that the underlying silent failure of ctx.openUrl on some Windows configurations is a separate issue that may require further investigation.

Example

The proposed fix involves modifying the dist/extensions/google/oauth.js file to print the URL unconditionally before attempting to launch the browser:

ctx.log(`\nOpen this URL in your browser:\n\n${authUrl}\n`);
try {
    await ctx.openUrl(authUrl);
} catch {
    // URL already printed; nothing else to do.
}

Notes

The fix sidesteps the underlying issue of silent failures in ctx.openUrl on some Windows configurations, which may require further investigation.

Recommendation

Apply the proposed workaround by printing the authorization URL unconditionally before attempting to launch the browser, as it provides a reliable fallback for users experiencing silent launch failures.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING