fix(gateway,proxy): bypass Windows proxy for localhost gateway connections#73474
fix(gateway,proxy): bypass Windows proxy for localhost gateway connections#73474DhtIsCoding wants to merge 6 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR attempts to fix Windows proxy env vars being inherited by WSL and breaking gateway loopback connections. The
Confidence Score: 3/5Not safe to merge — the localhost path throws an error and the env-var restore is broken, corrupting proxy state for non-gateway connections. Two independent P1 defects on the changed code paths: one causes an exception for the primary use-case (localhost gateway URLs) and the other permanently destroys http_proxy/https_proxy process-wide after each bypass call. Both need fixes before merging. src/infra/net/proxy/proxy-lifecycle.ts (both the isGatewayLoopbackControlPlaneUrl guard and the duplicate-key save/restore); src/gateway/client.ts (consequential on the guard fix). Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/gateway/client.ts
Line: 102-103
Comment:
**`localhost` bypass throws instead of connecting**
`createDirectGatewayAgent` now returns a non-`undefined` agent for `ws://localhost:18789` (via `isLoopbackHost`), which causes `dangerouslyBypassManagedProxyForGatewayLoopbackControlPlane` to be called with that URL. However, `isGatewayLoopbackControlPlaneUrl` in `proxy-lifecycle.ts` (line 374) still calls `isLoopbackIpAddress(url.hostname)`, which returns `false` for `"localhost"`, so the function throws `"proxy: dangerous Gateway control-plane bypass is loopback-only"`. Any connection attempt to `ws://localhost:18789` will fail with an exception rather than being fixed.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/infra/net/proxy/proxy-lifecycle.ts
Line: 392-414
Comment:
**Duplicate keys corrupt save/restore for `http_proxy` and `https_proxy`**
`ALL_PROXY_ENV_KEYS` already includes `"http_proxy"` and `"https_proxy"` (via `PROXY_ENV_KEYS`). The first save-and-delete loop correctly snapshots their values and removes them from `process.env`. The second loop then overwrites `savedProxyEnv["http_proxy"]` and `savedProxyEnv["https_proxy"]` with `undefined` (the env vars are already gone), so the restore loop for `ALL_PROXY_ENV_KEYS` sees `undefined` and calls `delete` instead of restoring the original values. After any gateway bypass call, `http_proxy` and `https_proxy` are permanently cleared for all subsequent connections in the process — silently breaking system proxy routing for non-gateway traffic.
The `lowercaseKeys` array should only contain keys not already present in `ALL_PROXY_ENV_KEYS`. The only genuinely new key here is `"all_proxy"`.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix(proxy): clear system proxy env vars ..." | Re-trigger Greptile |
| if (!isLoopbackHost(hostname)) { | ||
| return undefined; |
There was a problem hiding this comment.
localhost bypass throws instead of connecting
createDirectGatewayAgent now returns a non-undefined agent for ws://localhost:18789 (via isLoopbackHost), which causes dangerouslyBypassManagedProxyForGatewayLoopbackControlPlane to be called with that URL. However, isGatewayLoopbackControlPlaneUrl in proxy-lifecycle.ts (line 374) still calls isLoopbackIpAddress(url.hostname), which returns false for "localhost", so the function throws "proxy: dangerous Gateway control-plane bypass is loopback-only". Any connection attempt to ws://localhost:18789 will fail with an exception rather than being fixed.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/client.ts
Line: 102-103
Comment:
**`localhost` bypass throws instead of connecting**
`createDirectGatewayAgent` now returns a non-`undefined` agent for `ws://localhost:18789` (via `isLoopbackHost`), which causes `dangerouslyBypassManagedProxyForGatewayLoopbackControlPlane` to be called with that URL. However, `isGatewayLoopbackControlPlaneUrl` in `proxy-lifecycle.ts` (line 374) still calls `isLoopbackIpAddress(url.hostname)`, which returns `false` for `"localhost"`, so the function throws `"proxy: dangerous Gateway control-plane bypass is loopback-only"`. Any connection attempt to `ws://localhost:18789` will fail with an exception rather than being fixed.
How can I resolve this? If you propose a fix, please make it concise.| const lowercaseKeys = ["http_proxy", "https_proxy", "all_proxy"]; | ||
| for (const key of lowercaseKeys) { | ||
| savedProxyEnv[key] = process.env[key]; | ||
| delete process.env[key]; | ||
| } | ||
| try { | ||
| return run(); | ||
| } finally { | ||
| for (const key of ALL_PROXY_ENV_KEYS) { | ||
| if (savedProxyEnv[key] !== undefined) { | ||
| process.env[key] = savedProxyEnv[key]; | ||
| } else { | ||
| delete process.env[key]; | ||
| } | ||
| } | ||
| for (const key of lowercaseKeys) { | ||
| if (savedProxyEnv[key] !== undefined) { | ||
| process.env[key] = savedProxyEnv[key]; | ||
| } else { | ||
| delete process.env[key]; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Duplicate keys corrupt save/restore for
http_proxy and https_proxy
ALL_PROXY_ENV_KEYS already includes "http_proxy" and "https_proxy" (via PROXY_ENV_KEYS). The first save-and-delete loop correctly snapshots their values and removes them from process.env. The second loop then overwrites savedProxyEnv["http_proxy"] and savedProxyEnv["https_proxy"] with undefined (the env vars are already gone), so the restore loop for ALL_PROXY_ENV_KEYS sees undefined and calls delete instead of restoring the original values. After any gateway bypass call, http_proxy and https_proxy are permanently cleared for all subsequent connections in the process — silently breaking system proxy routing for non-gateway traffic.
The lowercaseKeys array should only contain keys not already present in ALL_PROXY_ENV_KEYS. The only genuinely new key here is "all_proxy".
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/net/proxy/proxy-lifecycle.ts
Line: 392-414
Comment:
**Duplicate keys corrupt save/restore for `http_proxy` and `https_proxy`**
`ALL_PROXY_ENV_KEYS` already includes `"http_proxy"` and `"https_proxy"` (via `PROXY_ENV_KEYS`). The first save-and-delete loop correctly snapshots their values and removes them from `process.env`. The second loop then overwrites `savedProxyEnv["http_proxy"]` and `savedProxyEnv["https_proxy"]` with `undefined` (the env vars are already gone), so the restore loop for `ALL_PROXY_ENV_KEYS` sees `undefined` and calls `delete` instead of restoring the original values. After any gateway bypass call, `http_proxy` and `https_proxy` are permanently cleared for all subsequent connections in the process — silently breaking system proxy routing for non-gateway traffic.
The `lowercaseKeys` array should only contain keys not already present in `ALL_PROXY_ENV_KEYS`. The only genuinely new key here is `"all_proxy"`.
How can I resolve this? If you propose a fix, please make it concise.|
Codex review: needs maintainer review before merge. Keep this PR open. Current main still implements and documents literal-loopback-IP-only Gateway proxy bypass behavior, while this PR proposes a focused but security-sensitive broadening to Maintainer follow-up before merge: Keep this PR open for normal maintainer review. If maintainers accept Best possible solution: Keep this PR open for normal maintainer review. If maintainers accept Acceptance criteria:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against a1197b907524. |
…tions - Use isLoopbackHost instead of isLoopbackIpAddress in createDirectGatewayAgent - Use isLoopbackHost in isGatewayLoopbackControlPlaneUrl for proper localhost handling - Clear proxy environment variables when OpenClaw proxy is inactive but system proxy may be set - Remove duplicate keys from lowercaseKeys to prevent env var save/restore corruption Fixes: Windows proxy inherited by WSL causing gateway connection timeouts
1d573c9 to
b503580
Compare
ac2dd10 to
e384d7c
Compare
|
Thanks for working on this. The direction makes sense for the Windows plus WSL failure mode: both I want one follow-up before this is merge-ready. In Once that is tightened, the localhost boundary itself looks reasonable. The important security constraint is still preserved because non-loopback URLs continue to throw |
|
Thanks @DhtIsCoding. I landed the accepted version of this directly on What landed:
Verification run before landing:
|
Summary
Fix an issue where OpenClaw CLI fails to connect to the local gateway at ws://127.0.0.1:18789 or ws://localhost:18789 when Windows proxy environment variables are inherited by WSL.
Problem
When running OpenClaw CLI in WSL on Windows, connections to the local gateway timeout because the HTTP/WebSocket client respects the Windows proxy settings even for localhost connections.
Changes
src/gateway/client.ts
src/infra/net/proxy/proxy-lifecycle.ts
Testing
Build: pnpm build - PASSED