Repository navigation
Description
Summary
When using google-vertex (Gemini models) in OpenCode V2, any turn that generates a large tool call (write or edit of ~300+ lines) consistently fails mid-stream with ECONNRESET: The socket connection was closed unexpectedly after exactly 60.0 seconds (59,997 ms–59,999 ms) of stream silence.
Because @opencode/ai's Gemini protocol (packages/ai/src/protocols/gemini.ts) does not enable Vertex AI's toolConfig.functionCallingConfig.streamFunctionCallArguments: true, Vertex AI buffers the entire functionCall.args JSON payload on the server until tool generation completes, leaving the SSE connection completely silent (0 bytes sent) for >60s until the socket is reset.
Environment
- opencode version: 2.0.32
- OS: Windows_NT 10.0.26200 (win32 x64)
- Terminal: OpenCode Desktop App (
OPENCODE_CLIENT=desktop) - Shell: PowerShell /
C:\Windows\system32\cmd.exe - Install/channel: latest
- Active plugins: None
Reproduction
- Connect
google-vertexand select a Gemini model (e.g.,google-vertex/gemini-4-argonwithmediumorhighvariant). - Ask the agent to rewrite or make a large
edit/writeto a ~300–800 line file (e.g., a 30 KB.tsxor.cssfile) in a single tool call. - Observe that reasoning (
thought: true) chunks stream normally, then the stream goes silent while the model generates thefunctionCallarguments. - Exactly 60.0 seconds after the last reasoning chunk is received, the connection fails with
ECONNRESET. - Instructing the model in the same session to split the edits into smaller (<100 line)
edittool calls immediately succeeds in 10–25s per step without any errors.
Expected Behavior
Large write / edit tool calls on google-vertex should stream or complete without dropping the connection at 60 seconds.
Actual Behavior
Every attempt to generate a large tool call fails after 60.0 seconds of silence following the last streamed chunk, repeating across automatic retries until the session stops with:
ECONNRESET: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()
Exact timing evidence from exported session messages
In exported session data (opencode session export), the timestamp delta between the last streamed reasoning part (time.created) and the stream failure (time.completed) is consistently 60,000 ms:
-
Example 1 (
msg_148493db3001FM5UDeUCkhE1YS):- Reasoning part
created:1792214253681 - Reasoning part
completed(error):1792214313680 - Delta:
59,999 ms(59.999s) - Error:
Connection lost while reading the response: ECONNRESET: The socket connection was closed unexpectedly. For more information, pass 'verbose: true' in the second argument to fetch()(status: 200)
- Reasoning part
-
Example 2 (
msg_14851bf7e001TSJwJhhPOdMT54):- Reasoning part
created:1792214691931 - Reasoning part
completed(error):1792214751928 - Delta:
59,997 ms(59.997s)
- Reasoning part
-
Example 3 (
msg_14824a81a001OJnCtbh3XkSvVu— no reasoning chunk emitted before tool generation):- Message
created:1792212292575 - Message
completed(error):1792212352733 - Delta:
60,158 ms(60.158s)
- Message
Server log stack trace (opencode.log)
level=ERROR message="Failed to drain Session" cause="AI.Error: ECONNRESET: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()
at a (B:/~BUN/root/chunk-fgtjesq4.js:6:4214)
...
at SessionStep.attempt (B:/~BUN/root/chunk-z20sjgqk.js:382:3574)
at SessionRunner.runStep (B:/~BUN/root/chunk-z20sjgqk.js:382:2672)
at SessionRunner.drain (B:/~BUN/root/chunk-4e2rqd16.js:18:2552) {
[cause]: AI.Error.Transport: ECONNRESET: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch() {
[cause]: TypeError: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()
}
}" role=server
Additional Context
Root Cause Analysis
-
Vertex AI Function Call Buffering:
Unlike Anthropic (input_json_delta) and OpenAI (function_call_arguments.delta), Vertex AI'sstreamGenerateContent?alt=sseendpoint buffers the entirefunctionCall.argspayload on the server by default and sends zero SSE bytes while the model generates the tool arguments. When generating a largewriteoredit(oldString+newString), generation easily exceeds 60 seconds of dead silence on the socket. -
streamFunctionCallArgumentsin Vertex AI:
Vertex AI supports"streamFunctionCallArguments": trueintoolConfig.functionCallingConfig(which streams incrementalpartialArgswithwillContinue: trueonfunctionCallparts, as used in@ai-sdk/google-vertex). Currently,packages/ai/src/protocols/gemini.tsonly definesmodeandallowedFunctionNamesinGeminiToolConfig, and only emitstoolConfigwhenrequest.toolChoiceis explicitly set. -
Why PR fix(core): disable Bun's fetch idle timeout for provider requests #55007 / fix(ai): raise default chunk timeout to fifteen minutes #55032 did not prevent this:
Even on v2.0.32 (which includes fix(core): disable Bun's fetch idle timeout for provider requests #55007 setting{ timeout: false }onFetchHttpClient.RequestInitand fix(ai): raise default chunk timeout to fifteen minutes #55032 raisingchunkTimeoutto 15m), the underlying connection toaiplatform.googleapis.comis still reset after 60.0s of total silence whenstreamFunctionCallArgumentsis not active.
Activity
opencode-agent commentedon Oct 17, 2026
Thanks for the detailed report and the timing data. I couldn't reproduce this because it needs a real Google Vertex account, which this sandbox doesn't have.
I did check our side of the connection. Bun's
fetchwithtimeout: falseread an SSE stream with a 90-second silent gap from a local mock server, with no reset. That test was on Linux, not Windows. I also found no 60-second timeout in OpenCode's model streaming code. So the 60-second reset probably comes from Google's side or from something on your network, not from OpenCode cutting the connection. EnablingstreamFunctionCallArgumentsfor Gemini, as you suggest, would keep data flowing during long tool calls and should avoid the silence.This needs a maintainer with Vertex access to confirm. Until then, splitting the work into smaller edits, as you found, is the workaround.
Related (they share the error message but have different causes): #53829, #51537, #46088.