fix(core): avoid keep-alive socket reuse during OAuth token exchange#28103
fix(core): avoid keep-alive socket reuse during OAuth token exchange#28103ryium wants to merge 1 commit into
Conversation
On Node >= 24.17.0 a regression in http.Agent socket reuse ("fix response queue poisoning") makes node-fetch throw a spurious ERR_STREAM_PREMATURE_CLOSE ("Premature close") when a pooled keep-alive socket is reused. This broke "Sign in with Google": the authorization-code -> token exchange goes through google-auth-library -> gaxios -> node-fetch over Node's global keep-alive agent.
Pass an agent with keepAlive: false to the OAuth2Client transporter when no proxy is configured, so each request uses a fresh connection and avoids the poisoned-socket race. The same client backs the Code Assist API calls, so the whole flow is covered. The proxy path is left unchanged because passing our own agent would make gaxios ignore the proxy.
Refs nodejs/node#63989, google-gemini#28072, google-gemini#26411
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical regression in Node.js versions 24.17.0 and later, where socket reuse in the global http.Agent causes OAuth token exchange failures. By forcing a fresh connection per request via a custom https.Agent when no proxy is configured, the fix restores login functionality for users on newer Node.js versions while maintaining support for existing proxy environments. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
|
📊 PR Size: size/M
|
There was a problem hiding this comment.
Code Review
This pull request introduces a workaround for a Node.js regression (nodejs/node#63989) where socket reuse in http.Agent causes node-fetch to throw a spurious ERR_STREAM_PREMATURE_CLOSE error during OAuth token exchange. To resolve this, HTTP keep-alive is disabled by configuring a custom https.Agent with keepAlive: false when initializing the OAuth2Client, unless a proxy is configured. Corresponding unit tests have been added to verify this behavior under both proxy and non-proxy configurations. There are no review comments, so I have no feedback to provide.
Summary
OAuth "Sign in with Google" fails on Node.js >= 24.17.0 with
Failed to exchange authorization code for tokens: ... Premature close.A Node
http.Agentsocket-reuse regression makesnode-fetchthrow a spuriousERR_STREAM_PREMATURE_CLOSEwhen a pooled keep-alive socket is reused, breakinglogin for everyone on current Node. This change makes the OAuth token exchange
use a fresh (non-keep-alive) connection so sign-in works again without
downgrading Node.
Details
The token exchange runs through
google-auth-library→gaxios→node-fetch,which uses Node's global keep-alive agent. On Node >= 24.17.0 the "fix response
queue poisoning in http.Agent" change exposes a reuse race that surfaces as a
false "Premature close" (see nodejs/node#63989). This matches reports that
downgrading to Node 20 makes login work again.
Fix: in
initOauthClient(packages/core/src/code_assist/oauth2.ts), passnew https.Agent({ keepAlive: false })to theOAuth2Clienttransporter when noproxy is configured, forcing a fresh connection per request and avoiding the
poisoned-socket reuse. The proxy path is left unchanged, since supplying our own
agent would make gaxios ignore the configured proxy. Because the same client
also backs the Code Assist API calls, the whole code-assist flow benefits.
Related Issues
How to Validate
node -v).npm run generate && npm run build --workspace=@google/gemini-cli-core.npx vitest run packages/core/src/code_assist/oauth2.test.tskeepAlive: falseagent whenno proxy is set, and keeps
proxy(no custom agent) when a proxy is set.authorization-code → token exchange completes instead of failing with
"Premature close".
HTTPS_PROXYset /--proxyconfigured, login stillroutes through the proxy as before.
Pre-Merge Checklist