Repository navigation
Description
Summary
On Windows, opencode's on-demand ripgrep download does not honor the system proxy configured in Windows Internet Settings (WinINET registry) — it only respects HTTP_PROXY/HTTPS_PROXY env vars. On networks where GitHub is only reachable through that proxy, the download fails, the failure is reported to the model only as the generic ripgrep execution failed (nothing in the server log after the downloading ripgrep INFO line), and the resolver never recovers for the lifetime of the server process: grep/glob/find stay broken until a full restart, even after a valid rg.exe is manually placed where the resolver looks.
Environment
- opencode version: v2.0.12 (npm global
@opencode/cli) - OS: Windows_NT 10.0.26200 (win32 x64)
- Terminal: VS Code (
TERM_PROGRAM=vscode) - Shell: agent shell runs PowerShell 5.1 (
ComSpecis cmd.exe) - Install/channel: npm, release
- Active plugins: none configured
Reproduction
- Windows machine where
github.comis only reachable via a local proxy configured in Windows Internet Settings (WinINET registry:ProxyEnable=1,ProxyServer=127.0.0.1:7890), with noHTTP_PROXY/HTTPS_PROXYenv vars set. (netsh winhttp show proxyshows direct;Invoke-WebRequestto the same release URL returns HTTP 200 because .NET honors the registry proxy — the network itself is fine.) - Ensure
rgis not onPATHand%USERPROFILE%\.cache\opencode\bin\rg.exedoes not exist. - Start opencode and trigger the grep tool.
- Server log (
~/.local/share/opencode/log/opencode.log) shows:…and then nothing: no error entry,level=INFO message="downloading ripgrep" url=https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-x86_64-pc-windows-msvc.zip%USERPROFILE%\.cache\opencode\binstays empty. - The tool result returned to the model is only:
ripgrep execution failed— no hint that the download was the failing step. - Manually place a known-good
rg.exe(verified runnable directly and spawnable from Nodechild_process) at%USERPROFILE%\.cache\opencode\bin\rg.exeand call grep again:- still
ripgrep execution failed - no new
downloading ripgrepentry (resolver does not re-run) - no process spawn for rg
- still
- Only after fully exiting opencode (including the
serve --servicebackend) and relaunching does grep work — the freshly placed binary is picked up on the first call, no network needed.
Expected Behavior
- The download should either honor the OS proxy configuration or at minimum honor
HTTPS_PROXY-style env vars consistently, and fail loudly (the underlying cause should reach the log / tool error). - A resolver failure should not persist for the process lifetime: subsequent calls should retry, and a manually-provided binary on disk should be picked up without a restart.
Actual Behavior
After the failed/hung download, every subsequent grep/glob call fails instantly with ripgrep execution failed. No retry is logged, no rg process is ever spawned, and manually fixing the binary on disk has no effect until the app is restarted. This made the root cause (proxy-blind download) very hard to diagnose: the error message points at "ripgrep execution" while the real failure is a network fetch several layers earlier.
Additional Context
- Affected code (v2.0.12):
packages/core/src/ripgrep/binary.ts— resolver wrapped inEffect.cached:which()→<cache>/bin/rg.exe→ download via effectHttpClient(no proxy options, no visible timeout).Effect.cachedexplains why the failed/pending computation is never retried for the process lifetime.packages/core/src/ripgrep.ts— every non-Error/InvalidPatternErrorfailure is mapped tofailure("ripgrep execution failed", cause), and thecausenever reaches the model or the log.
- Timeline observed in the log: one
downloading ripgrepentry per fresh failure on day 1 and day 2 (retries did happen while each fetch failed fast), then after the final attempt no further attempts were ever logged for that server process — consistent with the cached resolver holding a failed/pending result. - Workaround that works: download the ripgrep zip out-of-band (through the system proxy), extract, and copy
rg.exeto%USERPROFILE%\.cache\opencode\bin\rg.exe, then restart opencode. Alternatively putrg.exeonPATHbefore first use. - Possible directions:
- pass proxy config / honor env+OS proxy in the ripgrep download client, and add a fetch timeout
- log the download failure at ERROR level and include the underlying cause in the mapped tool error
- invalidate the cached resolver on failure (or re-check the disk path on every call) so a manually-placed binary is adopted without a restart
Activity
aron-intframe commentedon Sep 24, 2026
Confirmed the "never recovers" half at the source level, and it is not proxy- or Windows-specific:
Effect.cachedatpackages/core/src/ripgrep/binary.ts:92memoizes the failure, so the resolver body never runs a second time and the<cache>/bin/rgcheck at line 98 is never re-evaluated.I mirrored the resolver (same order:
which()-><cache>/bin/rg->logInfo->http.execute) against the effect build this repo pins ([email protected]) and ran it:A is your steps 4-7 exactly: one failed download, then calls 2 and 3 still fail with the cached error,
resolver body ran 1x, disk checked 1x, so thergyou placed in the cache dir is never looked at. B is the identical body withoutEffect.cachedand recovers on the very next call, no restart.Two things the report does not cover yet.
C - you do not need a proxy to hit this.
ripgrep.ts:144races the program againstwaitForAbort(input.signal). If the first grep is cancelled while the download is still in flight, the interrupt is what gets memoized, and every later call in that process fails withAll fibers interrupted without error, which then maps toripgrep execution failed. That reproduces on Linux/macOS with a perfectly good network. Related:binary.ts:110-114has no timeout onhttp.execute, so a proxy that blackholes the connection parks the cached deferred instead of failing it, and every later caller waits on that same deferred.D - why the cause never reaches the log or the model.
ripgrep.ts:41isexport class Error extends Schema.TaggedErrorClass(...), which shadows the globalErrorfor the rest of that module. The download failure built atbinary.ts:113is a plainError, socause instanceof Errorat line 147 isfalseand it falls intofailure("ripgrep execution failed", cause). The cause is attached to the tagged error but nothing ever logs it, which is exactly the "nothing in the log after the INFO line" you saw.Minimal standalone repro for C, no Windows and no proxy needed:
The resolver body runs once and never again; call #1's interrupt is what the cache latches.
On the proxy half specifically, there is no proxy handling anywhere in the tree, so the download client is whatever the runtime's global
fetchdoes and nothing more:Any fix there has to install a dispatcher/agent explicitly; reading
HTTP_PROXY/HTTPS_PROXYin application code will not changeFetchHttpClienton its own.Worth knowing before anyone writes a patch: the resolver half is already filed as Bug 2 of the still-open #40623, and a fix for it ("single-flight memo that caches only successful resolutions") has been submitted twice and landed neither time. #41015 was auto-closed by the PR-standards bot for a missing linked issue, and #41028 was closed by its author for lack of review bandwidth, with the branch kept at
Yuxin-Qiao/opencode-fixfix/ripgrep-windows-resolution. Current main (5fcfb06) still hasEffect.cachedatbinary.ts:92. So the genuinely new content in this issue is the proxy blindness, the missing fetch timeout, and the swallowed cause; the "never recovers" part has a ready patch that just needs a review.Same shape bit us in our own scheduled agents: a failure that announces itself once and then latches, after which "broken" and "fine" emit identical output. The catalog entry we keep for it is in our repo (ours): https://github.com/aron-intframe/agent-guards#3-the-longer-something-rots-the-quieter-it-gets
fix: bound startup-path network waits for offline hosts
opencodejr commentedon Oct 28, 2026
Thanks for the detailed report. I could reproduce this, and it isn't limited to Windows or proxies: any failed ripgrep download is enough.
What I did on v2.0.12 (Linux): took
rgoffPATH, madegithub.comunreachable, then had the model callgrep.ripgrep execution failed.rginto~/.cache/opencode/bin/rg, the folder the resolver checks.ripgrep execution failed. No new download attempt is made and the binary on disk is never used.It behaves the same on the latest
dev(fa1260d). As you suspected, the resolver inpackages/core/src/ripgrep/binary.tsis wrapped inEffect.cached, which keeps the first failure until restart. The recording and script below show the reproduction.I couldn't test the Windows system-proxy part here. Until this is addressed, your workaround of putting
rg.exeonPATHor in the cache folder before starting OpenCode is the reliable option.recording.mp4
Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.Anthony14FR commentedon Nov 12, 2026
I'd like to take this. On current
devthe resolver inpackages/core/src/ripgrep/binary.tsis still wrapped inEffect.cached, so one failed download is memoized until restart. My plan is to cache only a successful resolution, so the next search retries after a failure, plus a regression test where the first attempt fails and the second succeeds. #51192 did this before and was closed by the stale-PR cleanup (no linked issue), not on review, so I'll link this issue this time. The Windows system-proxy part would be a separate change.