Skip to content

ai(google-vertex): ECONNRESET after exactly 60.0s during large tool calls (streamFunctionCallArguments not enabled) #56344

Open

Description

@mirucon

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

  1. Connect google-vertex and select a Gemini model (e.g., google-vertex/gemini-4-argon with medium or high variant).
  2. Ask the agent to rewrite or make a large edit / write to a ~300–800 line file (e.g., a 30 KB .tsx or .css file) in a single tool call.
  3. Observe that reasoning (thought: true) chunks stream normally, then the stream goes silent while the model generates the functionCall arguments.
  4. Exactly 60.0 seconds after the last reasoning chunk is received, the connection fails with ECONNRESET.
  5. Instructing the model in the same session to split the edits into smaller (<100 line) edit tool 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)
  • Example 2 (msg_14851bf7e001TSJwJhhPOdMT54):

    • Reasoning part created: 1792214691931
    • Reasoning part completed (error): 1792214751928
    • Delta: 59,997 ms (59.997s)
  • Example 3 (msg_14824a81a001OJnCtbh3XkSvVu — no reasoning chunk emitted before tool generation):

    • Message created: 1792212292575
    • Message completed (error): 1792212352733
    • Delta: 60,158 ms (60.158s)

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

  1. Vertex AI Function Call Buffering:
    Unlike Anthropic (input_json_delta) and OpenAI (function_call_arguments.delta), Vertex AI's streamGenerateContent?alt=sse endpoint buffers the entire functionCall.args payload on the server by default and sends zero SSE bytes while the model generates the tool arguments. When generating a large write or edit (oldString + newString), generation easily exceeds 60 seconds of dead silence on the socket.

  2. streamFunctionCallArguments in Vertex AI:
    Vertex AI supports "streamFunctionCallArguments": true in toolConfig.functionCallingConfig (which streams incremental partialArgs with willContinue: true on functionCall parts, as used in @ai-sdk/google-vertex). Currently, packages/ai/src/protocols/gemini.ts only defines mode and allowedFunctionNames in GeminiToolConfig, and only emits toolConfig when request.toolChoice is explicitly set.

  3. 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 } on FetchHttpClient.RequestInit and fix(ai): raise default chunk timeout to fifteen minutes #55032 raising chunkTimeout to 15m), the underlying connection to aiplatform.googleapis.com is still reset after 60.0s of total silence when streamFunctionCallArguments is not active.

Related issues: #53829, #51537, #46088

Activity

  1. opencode-agent commented on Oct 17, 2026

    @opencode-agent
    Contributor

    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 fetch with timeout: false read 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. Enabling streamFunctionCallArguments for 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions