Repository navigation
Description
Summary
On a network where the ISP's DPI drops/resets fragmented TLS ClientHellos, opencode hangs before it ever sends the inference request: no session, no stream, no error for 120+ seconds. The same request from curl on the same machine and same IP succeeds in ~0.2s. Works instantly when routed through a VPN.
Environment
- opencode version: 2.2.2 (latest channel)
- OS: Windows 10 Pro 26H2, build 26300.9818 (win32 x64)
- Terminal: xterm-256color (TERM set; TERM_PROGRAM/COLORTERM unset)
- Shell: C:\WINDOWS\system32\cmd.exe
- Install/channel: npm global install (
@opencode/cli), channellatest - Active plugins: none (
~/.config/opencode/opencode.jsonccontains only{"autoupdate": false})
Reproduction
- Use a network where TLS ClientHellos split across two TCP segments are dropped by DPI (observed on Rostelecom, Irkutsk, Russia; home IP, no VPN, no local proxy/TUN active).
- Ensure no VPN/proxy is running (
Get-NetAdaptershows only the physical Ethernet adapter; public IP is the ISP's). - Run:
opencode run --model opencode/deepseek-v4.1-flash-free "hi" - Observe: command produces no output and never completes (still running after 120s).
- Repeat the same run with a VPN enabled: it answers within a couple of seconds.
Expected Behavior
Either the request is sent and a response (or a normal HTTP error such as 401/429) is returned, or a clear network error is reported quickly. It should not hang indefinitely without any log line indicating where it stopped.
Actual Behavior
The CLI starts, spawns its local server, the server boots and emits provider.updated / model.updated / agent.updated events, and then nothing at all. There is no session line, no stream line and no error — for 120+ seconds:
23:37:16.915Z level=INFO run=e33c85a4 message="cli starting" version=2.2.2 channel=latest local=false args="[\"run\",\"--model\",\"opencode/deepseek-v4.1-flash-free\",\"hi\"]" role=cli
23:37:22.001Z level=INFO run=7c383666 message="cli starting" version=2.2.2 channel=latest local=false args="[\"serve\",\"--service\"]" role=cli
23:37:22.601Z level=INFO run=7c383666 message=event event.type=provider.updated ...
23:37:22.606Z level=INFO run=7c383666 message=event event.type=model.updated ...
23:37:22.607Z level=INFO run=7c383666 message=event event.type=agent.updated ...
(no further entries for 120s)
In other sessions the same environment instead produces, after ~19-20 seconds:
level=ERROR message="stream error" providerID=opencode modelID=deepseek-v4.1-flash-free
error.error="AI_APICallError: Cannot connect to API: ECONNRESET: The socket connection was closed unexpectedly."
Notably, curl on the very same machine, same IP, same moment:
$ curl -s -D - -o NUL -X POST -H "Content-Type: application/json" \
-d '{"model":"deepseek-v4.1-flash-free","messages":[{"role":"user","content":"hi"}]}' \
https://opencode.ai/inference/openai/v1/chat/completions
HTTP/1.1 400 Bad Request
Content-Length: 0
Server: cloudflare
x-opencode-log-id: 01a18a01-9bf6-7360-b0a1-61ec0c3cd719
$ curl -s -o NUL -w "%{http_code}" https://opencode.ai/inference/v1/models
200
So DNS, TCP and TLS from this host are fine, and the gateway responds normally. Only opencode's own fetch path fails.
Additional Context
Suspected cause: Bun's TLS stack sends a post-quantum hybrid key share (X25519MLKEM768), which makes the ClientHello roughly 1.5-1.9 KB and therefore spans two TCP segments. Networks with DPI that mishandles fragmented ClientHellos drop or reset it, so the handshake never completes. curl on Windows uses schannel with a small (~300 byte) ClientHello and is unaffected. Routing through a VPN hides the ClientHello inside the tunnel, which is why it works.
This is consistent with all observations:
curl(small ClientHello) works without VPN; opencode (large, fragmented ClientHello) does not.opencodeworks immediately over VPN.- Not model-specific:
deepseek-v4.1-flash-freeandbeam-freefail identically. npminstalls performed by opencode also fail withread ECONNRESETagainsthttps://registry.npmjs.orgin the same conditions (NpmInstallFailedError), i.e. the failure is in Bun's fetch, not in the inference client.- IPv6 is not involved (no global IPv6 address, no AAAA records).
- The endpoint is not blocked and not rate limited: unauthenticated
curlPOSTs return400,GET /inference/v1/modelsreturns200. An earlier429withRetry-Afterwas transient and later cleared.
Workarounds tried:
- VPN: works, but is the thing we want to avoid.
- Setting
HTTPS_PROXY/HTTP_PROXYto a local proxy: had no effect on the inference request (opencode still answered while the proxy pointed at a dead port), so it is not a usable workaround. BUN_CONFIG_VERBOSE_FETCH=1: only local127.0.0.1API calls are logged; the failing external fetch is not shown, which makes the hang hard to diagnose.
Suggestion: if possible, expose a way to disable the post-quantum key share (e.g. a TLS curves option or an env var), or add a connect/handshake timeout that surfaces a clear error instead of hanging silently. Other users behind DPI-heavy networks are likely affected.
Frequency: 100% reproducible on this network without a VPN; 0% with a VPN.
Happy to run any additional diagnostics you need.
Activity
opencodejr commentedon Oct 30, 2026
Thanks for the detailed report and the
curlcomparison. I could reproduce the silent hang, and it still happens on the latestdev(237a847).I can't recreate your ISP's DPI here, so I simulated it with a local server. It accepts the TCP connection, receives opencode's TLS ClientHello (about 1.48 KB) and never answers. A custom OpenAI-compatible provider points at that server, and the command is
opencode run --standalone --print-logs --model blackhole/m "hi".On 2.2.2 and on
dev:provider.updated/model.updated, with no session or stream line, just like your log.headerTimeout), opencode quietly opens a new connection and keeps waiting, still with no output.ECONNRESETerror only appears when the process is killed.So a stalled TLS handshake is treated like a slow model response and waited on silently, instead of failing quickly with a clear network error. The script below reproduces it.
This only confirms the hang. It doesn't confirm that your network drops the larger ClientHello. Your suggestions (an option to turn off the post-quantum key share, and logging of failed external fetches) are separate improvements for the maintainers to consider.
Reproduction script (repro.sh)
Run it:
bash repro.shMLGSW4G commentedon Oct 30, 2026
Same failure confirmed on several lines in Russia, starting Oct 29–30 UTC.
To be explicit up front: this is not an OpenCode defect. The connection is dropped during the TLS handshake, before any HTTP request is sent, so nothing in the client's request handling is involved — the same failure hits any client that offers a post-quantum key share, including Chrome and Edge.
Every affected line reported so far is Rostelecom: Irkutsk ×2 (this report, and the reply in #60455), Perm ×1, and Ulyanovsk in #60518. That is a sample artefact as much as a finding — Rostelecom is the largest provider, and DPI filtering of this kind is mandatory across Russian operators, so other ISPs may simply not have this rule deployed yet. If you are on another provider and see the same split (browser and Bun failing while
curl.exereturns 200 on the same host at the same moment), please add your ISP and city here.On all affected lines the browser cannot load
opencode.aiand opencode (Bunfetch) times out or returnsECONNRESET, whilecurl.exeagainst the same host from the same machine at the same moment returns 200 — so the host is reachable and the reset happens inside the TLS handshake.Hello-level measurement. On one affected line (Perm), with a host that showed the identical failure temporarily removed from the DPI-bypass list, using
openssl s_client -tls1_3 -servername <host> -groups <group>:X25519MLKEM768(post-quantum, ~1.5 KB)X25519(classical, ~300 B)Same host, same edge IP, same second, three rounds each.
opencode.aishows the same split (Bun times out,curlreturns 200) but was not A/B'd at hello level. This varies the key share and the hello size together, so it does not separate "PQ key share present" from "hello exceeds one TCP segment" — but it does rule out the server, the IP and plain SNI matching, since a classical hello with the same SNI passes.That is consistent with the simulation above, and it also explains why the browser is affected: Chrome and Edge offer the PQ key share by default, while Windows
curl(Schannel) does not. On the same line, other hosts behaved the same way, whileapi.opencode.aiandapi.github.comwere fine.Workaround. Add the affected hostnames to the DPI-bypass hostlist. With
opencode.ailisted, it returns 200 through Bun on a direct connection with no VPN.Not the same as the earlier reports. #59291 (resets on long or complex responses), #59786 and #54160 (stale keep-alive reuse) all fail after the connection is established; this one fails during the handshake. #58854 ("TLS handshake error", Oct 25) is closest in phase but has no ISP or geolocation data, so it cannot be classified either way.
The only client-side item here is failure reporting, not connectivity: the 120 s silent hang with no log line is worth fixing regardless of this network condition, because failing fast and naming the host would have made this a one-line diagnosis instead of a day of guessing.
MLGSW4G commentedon Oct 30, 2026
Follow-up: hello size, the version split, and a client-side escape hatch.
Additional per-name evidence on the same line (Rostelecom, Perm). With the host removed from the DPI bypass, so the rule is active for it:
curl.exemodels.devERR 23timeoutregistry.npmjs.orgERR 23timeoutWith
fetch(url, { tls: { ecdhCurve: 'X25519:P-256' } })the same host returns 200 on the same line, same minute, no VPN. So the rule is per name, and it is the handshake shape that decides.Where the shape changes. ClientHello sizes measured locally against
openssl s_server -trace, i.e. bytes on the wire:curl.exe(Schannel)x25519x25519x25519X25519MLKEM768firstX25519MLKEM768ecdhCurve: 'X25519'x25519ecdhCurve: 'X25519:P-256'x25519That explains the version split in #60518: 1.18.12 pins
[email protected], while 1.19.5 and 2.2.2 both pin[email protected]. The default changed in Bun 1.4.1 — the same release rewrote its usockets TLS layer (packages/bun-usockets/src/crypto/openssl.c), and the onlySSL_CTX_set1_groups_listcall there is the one behindecdhCurve, so the default comes from the bundled BoringSSL. The boundary is precise: 1.19.3 is the last release on[email protected], 1.19.4 was the first on[email protected], and every v2 release is on 1.4.2 or newer — so 1.19.3 is the newest build that works on an affected line. Those runtime strings were read from the published binaries, not just from the build pin.Using the escape hatch.
tls.groupsis ignored by Bun'sfetch;ecdhCurveis what reaches the group list. The names must be BoringSSL's (P-256, notsecp256r1), and a single group is not safe:registry.npmjs.orgfails withX25519alone and works withX25519:P-256.I would not make that the default. It drops the post-quantum half of the handshake for every connection, which is a poor trade for a coding agent. A retry with the classical group list on handshake failure or timeout — sticky per host, logged — or an explicit opt-in setting keeps PQ wherever it works and degrades only the path that needs it.
To be explicit: this is a mitigation for a network rule, not a fix. If the rule starts matching small hellos as well, it stops working.
Happy to send a PR for the retry or the knob if you agree on the shape — I would rather not guess at provider-wide TLS defaults.
MLGSW4G commentedon Oct 30, 2026
Confirming the proxy note in the report, with one version split that matters:
HTTPS_PROXYset to a dead portfetchECONNREFUSED— honouredCannot connect to API: Unable to connect— honouredSo the observation that a proxy has no effect holds for v2, but not for v1 — plain Bun and v1 both honour the variable. One related trap: in v2, setting
HTTPS_PROXYwithoutNO_PROXY=localhost,127.0.0.1also routes the CLI's own local server calls through the proxy, which hangs the run.That means a proxy is not a workaround for 2.2.x today; only a local DPI bypass (admin) or a client-side change works there.
b0dhi88 commentedon Oct 30, 2026
Same failure from a non-Bun stack, if useful as another data point: pi 1.4.0 on Node 24.20.0 (OpenSSL 3.5.7, undici 7.29.0), provider
opencode-go(deepseek-v4.1-flash), OpenAI-compatible endpoint onopencode.ai.Symptom in the same window (Oct 30, ~08:27–08:37 UTC):
Request timed out.after a constant 10.0–10.5 s,usage: 0, 3 retries. No request ever reaches the provider.Where it stalls:
connectTimeout.tls.connectagainst the same host: 5–11 of 16 attempts succeed, the rest time out at 10 s. TLS 1.2: 48/48. TLS 1.3 withecdhCurve: 'X25519'/'P-256': 100%.curlandopenssl s_client -tls1_3from the same machine at the same moment: 20/20, ~0.25 s.node --trace-tls: ClientHello 1579 B by default vs 347 B with classical groups;key_sharealone is 1258 B (X25519MLKEM768). So this is not Bun-specific — Node 24 / OpenSSL 3.5 has the same default.A/B through undici (fresh connection per request): 13/30 successes by default, 25/25 with
tls.DEFAULT_ECDH_CURVE = 'X25519:P-256'in the same process.Relevant for the proposed fix shape: pi has no connect-timeout knob and declined to add one (#11115, PR #11515 — "waiting longer does not fix that path"), so a client-side retry with classical groups (sticky per host) or an explicit opt-in would cover more than Bun. I currently use the one-line
tls.DEFAULT_ECDH_CURVEworkaround. Happy to test a patch.MLGSW4G commentedon Oct 30, 2026
This is the most useful data point here — it removes the "Bun-specific" framing and quantifies the intermittency.
X25519MLKEM768by default too (1579 B vs 347 B), so any modern client — Chrome, Bun, Node 24+, OpenSSL 3.5 — hits this, while Schannelcurl(438 B) does not. The 13/30 also explains the "retrying fixed it" and "another client fixed it" reports: that's a biased coin flip, not a workaround.tls.DEFAULT_ECDH_CURVE = 'X25519:P-256'being one line and process-wide means an opencode-side change does not have to wait on Bun or Node — it can set the classical group list for provider fetches, or on a retry, and cover both runtimes.Agreed on sticky retry over a longer timeout: a longer timeout only makes the coin flip slower. The smallest useful patch is: on a handshake timeout, retry once per host with classical groups, and remember the result for that host.
zugmaster commentedon Oct 30, 2026
Coming from #60568 (same symptom class, different ISP): I can confirm the "larger ClientHello" part of this hypothesis with a controlled test, so it doesn't have to stay an assumption.
On the affected machine the trigger is exactly the post-quantum key share, and it only matters for destination SNIs that are not on the carrier's pass-through list.
Fingerprint boundary
Same request, same machine, same minute, only the emulated TLS fingerprint changes (
curl_cffi):Stepwise scan on
opencode.ai: chrome99, 100, 101, 104, 107, 110, 116, 119, 120, 123 all succeed; chrome124, 131, 136 all time out. Safari profile succeeds. curl and Node succeed. Chrome 124 is the release that enabled the PQ key share by default.Hello size
Captured with
stracefrom the real request, parsed per extension:key_shareis 1258 bytes of it — group0x11ec(X25519MLKEM768) with a 1216-byte key, plus 32 bytes for X25519. So 1216 of 1502 bytes, ~81% of the handshake, is the PQ key.key_shareis only X25519, 32 bytes.With a 1500-byte MTU (1460 MSS) the 1497-byte record cannot fit one segment and goes out as two.
Vantage point
Same
Bun.connect({tls:true})script:open, noerrorPlain TCP to the same host opens in 45 ms, so it's not routing or DNS.
What this means
The block is content-dependent and destination-dependent: a PQ ClientHello passes to well-known hosts and is silently dropped for others. It is not specific to Bun — a current Chrome or Firefox profile fails the same way, Bun is just the transport that opencode happens to use.
That reinforces the suggestions already made here: an option to disable the post-quantum key share (or a non-PQ transport for internal requests) would actually work on such networks, while retries would not — in a sweep of 8 attempts the same Bun fetch to
opencode.aifailed 8 times, while the non-PQ profile succeeded every time.taishi-sama commentedon Oct 30, 2026
As (temporary) alternative solution it also possible to MITM opencode, so it would use OpenSSL cypher+crypto not blocked by DPI:
Be warn that opencode is pretty strict and use even cypher differences to detect free models abuse, so it is gonna work only with Opencode Go/Opencode Zen
1. Create the CA + the classical-groups config
2. Spawn the proxy
OPENSSL_CONF="$HOME/.mitmproxy/openssl-classical.cnf" \ uvx --from mitmproxy mitmdump --mode regular@8080OPENSSL_CONFmust be set on this process: the proxy's upstream handshake iswhat the DPI sees now.
--from mitmproxyis required because themitmproxybinary itself is the TUI.
3. Point opencode at it
HTTPS_PROXY=http://127.0.0.1:8080 NO_PROXY= \ NODE_EXTRA_CA_CERTS="$HOME/.mitmproxy/mitmproxy-ca-cert.pem" \ opencode serveRive116 commentedon Oct 30, 2026
help me please, i dont know what to do.NoDPI didn't help; what should I try next? Checking for updates didn't yield any results.
MLGSW4G commentedon Oct 30, 2026
Try https://github.com/Flowseal/zapret-discord-youtube.
Rive116 commentedon Oct 30, 2026
Thank you very much, that helped. @MLGSW4G
tweenfoldkay-sudo commentedon Oct 30, 2026
Field follow-up from an affected line (Rostelecom, Irkutsk, RU) — root-cause validation, a no-VPN workaround, and one confusing error to not conflate
Confirming the diagnosis of this thread from a real affected line, plus two things that may help other users and the maintainers.
Environment
@opencode/cli, Bun-compiled binary, Bun 1.4.4 runtime)opencode.ai(andmodels.devis not filtered on this line)Measurements on the same machine / same line (today):
curl.exe(Windows Schannel) →opencode.aiHTTP 400in 0.65 s ✅HTTP 400in 0.65 s ✅fetch(= opencode's stack,X25519MLKEM768→ CHLO ≈ 1.5 KB, split into 2 TCP segments)HTTP 500in 1.8 s ✅ (server answered — body was{}, so 4xx/5xx is expected; the point is the handshake + HTTP round-trip completed)fetch(url, { tls: { ecdhCurve: "X25519:P-256" } })run)So the client-side escape hatch everyone proposed earlier (retry/downgrade to classical groups) works on the real affected line, and the proxy path (
HTTPS_PROXY) also fixes it — both without touching the network.The end-user workaround that worked end-to-end today (no VPN):
winws.exe).lists/list-general-user.txt:fetchtoopencode.ai/inference/openai/v1/chat/completions→HTTP 500in 1.8 s (handshake passed); TUI andopencode runrespond normally. Everything else on the machine stays untouched (only the SNIs from the list are desynchronized).One misleading error that is NOT this bug — worth documenting:
On the same line,
opencode runwith the default model can fail instantly (~2 s, no hang) with:Do not confuse it with the 2-minute stall: that message is a fast server-side reject from the provider edge (in our case the default
runmodel resolved to an OpenRouter-hosted model, and OpenRouter refuses RU-region IPs by policy — their site is also blocked in a browser from a RU IP; a TLS bypass cannot help, only a proxy can). Pinning a non-openrouter model fixed it instantly:after which
opencode runanswers in ~4 s. Rule of thumb for affected users: minutes-long silence ≈ fragmented PQ ClientHello DPI; instant reject ≈ provider geo/WAF policy.Suggestion for maintainers (in line with what others proposed):
Since opencode ships Bun's stack, a config-exposed TLS override or an automatic stall-fallback would rescue every affected user at once, e.g. an
"inference": { "tls": { "ecdhCurve": "X25519:P-256" } }option passed to the underlying Bunfetch, or a per-host retry that downgrades the key-share to classical groups after N seconds with no server response. The stall currently looks exactly like a "slow model", which makes it extra confusing in the UI.Happy to re-run any measurements on this line if it helps. Huge thanks to everyone in this thread — the diagnosis and the workaround saved several people here an RDDT/MW ticket today.
fylat commentedon Oct 30, 2026
Дисклеймер: Делюсь решением на свой страх и риск. Я не несу ответственности за вашу ОС, безопасность или возможные сбои. Отключение проверки сертификатов снижает защиту — не используйте в публичных сетях.
Проблема: Ошибка ECONNRESET из-за DPI Ростелекома. Провайдер режет TLS-рукопожатие, а OpenCode (Bun) не доверяет сертификату VPN.
Мое решение (работает без отката на 1.19.3 и без Zapret):
Включаю VPN (Happ) в режиме «Смешанный» (TUN/Mixed).
Создаю .bat файл ровно с таким содержимым:
bat
set HTTPS_PROXY=http://127.0.0.1:10809
set NODE_TLS_REJECT_UNAUTHORIZED=0
start "" "C:\opencode\OpenCode.exe"
Запускаю OpenCode только через этот .bat.
Результат: Ошибка ECONNRESET пропадает, бесплатные модели (DeepSeek) работают.
Это временный костыль, но он помогает.
d1m40fficial commentedon Oct 30, 2026
https://github.com/d1m40fficial/opencode-dpi-fix Всё работает
ailanatoliicomsupermega99-byte commentedon Oct 30, 2026
я жру трупы
AlexRicS commentedon Oct 30, 2026
Теперь при использовании ВПН или Запрета появляется эта ошибка... ( если это ошибка вообще )

Без ВПН и Запрета такого не было, всё работало нормально. Кто-то сталкивался с этим ?
(Не обращайте внимание на дату, скрин не мой, но ошибка точно такая же, только дата 30.10)
YourReflection commentedon Oct 31, 2026
"Workaround published: zapret as a Windows service, scoped to a hostlist\n\nInstead of waiting for a client-side fix, I packaged the approach from this thread as a one-click installer for Windows:\n\n**https://github.com/YourReflection/zapret-opencode-bypass**\n\nWhat it does:\n- wraps
winws(bol-van/zapret, official zapret-win-bundle binaries — downloaded at install time, nothing bundled) into a Windows servicewinws-opencode, start type auto, so the bypass survives reboots;\n- installs a 5-min watchdog scheduled task (runs as SYSTEM, restarts the service if it dies, e.g. after AV removesWinDivert64.sys);\n- uses the strategy tested earlier in this thread:--dpi-desync=fake,fakedsplit --dpi-desync-split-pos=1 --dpi-desync-fooling=badseqfor TLS + fake QUIC initial, hostlist =opencode.ai,models.dev,registry.npmjs.org(all subdomains match automatically, list is editable and preserved across reinstalls).\n\nVerified on an affected line (Rostelecom): TUI andopencode runanswer,models.devandregistry.npmjs.orgreachable through Bun\u0027\u0027s default PQ ClientHello, no VPN.\n\nNotes: it is not a VPN — no re-encryption, no third-party hops, only the listed SNIs are desynchronized. Full removal is one script (service_opencode_delete.cmd).\n\nStill hoping for the proper fix discussed above (classical-groups retry /ecdhCurveoption) — this is just the mitigation until then."opencode-agent commentedon Oct 31, 2026
Impact from our gateway telemetry. Because the connection breaks during the TLS handshake, the gateway logs no errors for these users. What it shows instead is Russian traffic disappearing. The figures below count distinct IPs that completed at least one generation, 12:00–24:00 UTC each day:
Go is paid and our free-tier policy changes don't touch it, so the Russia-only Go drop is the cleanest view of this connectivity break.
User feedback shows the same thing: reports of "ECONNRESET / socket closed unexpectedly" went from a 7-day median of about 1 a day to 24 in the last 24 h. Almost all of them are Windows Desktop users on Russian home ISPs, on 2.1.4 through 2.2.3, across Free, Go and Zen. Reports started at about 23:48 UTC on Oct 29 and jumped on the morning of Oct 30, Moscow time (#60846 and #60661 are the same issue). No OpenCode or Console deploy lines up with the start.
Possible fixes on the client side: send a smaller ClientHello (no post-quantum key share by default), or retry with a classic handshake after a reset during the handshake.
alexeyfadeev commentedon Oct 31, 2026
This morning the issue was still reproducible for me, but now opencode-go is accessible again from Russia (both, web-portal and LLM endpoint).
MLGSW4G commentedon Oct 31, 2026
Confirming,
opencode.aiis accessible for me now — default PQ ClientHello,no VPN, no desync (Bun 1.4.4, opencode 2.2.5).
But the DPI rule is still deployed. At the same time,
models.devon the samemachine and minute still shows the original signature:
curl.exe200 in 0.69 s,Bun default hello (X25519MLKEM768) times out at 19 s, same request with
ecdhCurve: 'X25519:P-256'returns 200 in 0.22 s.So only the
opencode.aiSNI stopped matching, not the rule. Model cataloguerefreshes can still stall silently, and this can flip back without notice —
today's reachability isn't evidence of a fix.
Samo-samo commentedon Nov 1, 2026
Data point from Turkey: bursty EPROTO/ECONNRESET on Bun/BoringSSL while curl and Node stay clean
Follow-up on this issue's topic, posted here as a general data point. My original report was #57875 ("Constant ECONNRESET error"), closed as not planned after one round of follow-up. It still reproduces as of Nov 1, 2026. I believe it belongs to this issue's class (DPI/middlebox interference with the large PQ ClientHello), but with an important twist: the failures arrive in bursts, affect the Bun stack while curl/Node stay clean, and the connection recovers completely between bursts.
Environment
Error signature by surface
EPROTO: The TLS handshake failed.ECONNRESET: The socket connection was closed unexpectedlyduring the same time windows.Both are transport-level failures of the bundled Bun runtime; the difference in message is consistent per surface.
Server side checked and clean:
status.opencode.aishowed "All systems operational" (Free inference 1.9 s · 174 tok/s at check time) while these client-side bursts were happening, and none of the recent server-side incidents in the past 90 days overlap the burst windows listed in the timeline below (closest: a Gateway/Free-inference "slow responses" Nov 1 13:07–13:14 UTC vs. our Nov 1 burst at 14:27; a "slow responses" Oct 30 22:28–22:35 vs. our Oct 30 burst 21:13–22:21). Also, a handshake that never completes is invisible to the gateway's own metrics — which is why bursts like this will never show up on the status page even while many users are affected.Within-session pattern and retries: even the diagnostic CLI session producing this report hit
EPROTOintermittently between turns; after a few manual retries it works again — for a while (minutes to hours), then the next burst arrives. During a burst, built-in retries eat multiple EPROTOs before a turn gets through: some turns recover only on the 7th–8th attempt, others never complete and surface inopencode.logasFailed to drain Session. On top of that there are failures that get retried silently and never reach the log — a turn that normally takes ~1 minute can burn 8 minutes and still end in error.Timeline from
opencode.log(UTC)All failures are
SessionRunner.drain → SessionStep.attempt → AI.Error.Transport; the connection dies at the provider call, on every model/provider tried.UNKNOWN_CERTIFICATE_VERIFICATION_ERROR(14:00)Outside these windows the same machine opens sessions and completes turns normally, sometimes for hours. This matches the "API call failed after 3 retries: Connection error... works after a few retries, then fails again" pattern from #57875.
A/B tests (Windows, same machine, same minutes)
Stack comparison:
curl.exe(Schannel)opencode.aiandmodels.devX25519MLKEM768too)ERR_SSL_DECRYPTION_FAILED_OR_BAD_RECORD_MAC(Oct 30) andERR_SSL_TLSV1_ALERT_DECODE_ERROR(Nov 1) onmodels.dev; otherwise OKecdhCurve: 'X25519:P-256'Two moments worth highlighting:
EPROTOandECONNRESETin the same minute. A Node PQ-default sweep toopencode.aiandmodels.devrun seconds later was 24/24 clean, andcurlwas 8/8 clean. So at that moment Bun's fetch was failing against a path that OpenSSL- and Schannel-based clients passed — consistent with the middlebox matching on ClientHello shape/fingerprint rather than pure size, since Node's PQ hello (~1.5 KB) also passed.opencode run --model opencode/glm-5.3-flash-free "hi"×4 → 4/4 OK in 2–6 s each. Nothing changed locally between the burst and this test.GoodbyeDPI A/B: Turkish users commonly run GoodbyeDPI. Observed both corruption errors (
BAD_RECORD_MAC,TLSV1_ALERT_DECODE_ERROR) while GoodbyeDPI was running; one clean 24/24 sweep while it was off. Burst failures also occurred while it was fully off (Nov 1, 14:27). Net: it neither fixes nor clearly worsens the bursts, and its current strategy does not help the fragmented PQ hello get through. If it matters: sample sizes are small and the process may have restarted between rounds, so treat this as weak evidence.What this adds to the analysis here
UNKNOWN_CERTIFICATE_VERIFICATION_ERRORseen mid-burst (Oct 20 14:00) matches the finding that handshake resets surface as certificate errors in Bun.Happy to run further diagnostics during the next burst window (e.g.
openssl s_clientA/B withX25519MLKEM768vsX25519onopencode.ai, or a targeted repeated-opencode runloop) — the bursts are frequent enough that it's easy to catch one.Re the latest update in this thread (some lines in Russia regaining access to
opencode.aiwhilemodels.devstill shows the original signature): from this line, the block is still active — the Nov 1 14:27–14:59 burst above happened on the default PQ ClientHello with no VPN or bypass running, andopencode.aiitself failed while Schannelcurland classical-groups Node passed from the same machine in the same minutes. So I'd read the reachability change there as an SNI-level rule edit, not a rollback — worth keeping the classical-groups retry/mitigation on the roadmap either way.galimzyanov-danil commentedon Nov 5, 2026
I have same issue with opencode go plus plan. How or when fix this? Downgrade to 1.19.3 not fix problem
neriousy commentedon Nov 12, 2026
This should be fixed in the latest version. If you still see ECONNRESET after updating, please open a new issue with your
opencode --version, OS, provider/model and any logs.