Skip to content

MCP HTTP client: Bun's hardcoded fetch timeout causes repeated retries and -32000 errors for long-running tool handlers #21741

Closed

Description

@josippapez

Problem

When an MCP tool handler takes a long time to resolve (e.g., an interactive prompt waiting for user input with a 1200s or infinite timeout), Bun's hardcoded ~5 minute fetch timeout fires before the tool completes. This causes:

  1. The fetch() call backing the StreamableHTTPClientTransport POST to abort with a TimeoutError
  2. OpenCode's MCP client retries the tool call on a new HTTP connection
  3. The MCP server receives a new request for the same tool, but the original SSE stream's cancel() callback has already removed the request ID from the SDK's internal _streamMapping
  4. send() throws "No connection established for request ID" → the agent sees -32000 Connection closed
  5. The tool result is permanently lost

Even with enableJsonResponse: true on the server (which eliminates SSE streams), the client-side fetch timeout still fires and causes unnecessary retries, producing duplicate tool call attempts in the agent's chat log.

Root cause

Bun hardcodes a ~5 minute fetch timeout that ignores AbortSignal.timeout() values longer than the hardcoded limit. This is a known Bun bug:

The MCP SDK's StreamableHTTPClientTransport passes fetch calls through to the runtime. When running on Bun, the runtime's internal timeout fires before the SDK's own AbortSignal timeout, causing an abort that the SDK interprets as a connection failure.

Fix

Bun supports a non-standard timeout option on fetch(). Passing timeout: false disables the hardcoded limit, allowing the SDK's own per-request AbortSignal to be the controlling timeout.

Suggested patch for packages/opencode/src/mcp/index.ts

Create a bunSafeFetch wrapper and pass it to all transport constructors:

// Bun hardcodes a ~5 min fetch timeout that ignores longer AbortSignal values.
// Passing `timeout: false` disables that limit so the SDK's own per-request
// timeout (from the user's `timeout` config) is the controlling timeout.
// See: https://github.com/oven-sh/bun/issues/16682
const bunSafeFetch = ((input: RequestInfo | URL, init?: RequestInit) =>
  // @ts-ignore Bun-specific option, not in standard RequestInit
  fetch(input, { ...init, timeout: false })) as typeof globalThis.fetch

Then pass fetch: bunSafeFetch to all three transport constructor sites:

 transport: new StreamableHTTPClientTransport(new URL(mcp.url), {
   authProvider,
   requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
+  fetch: bunSafeFetch,
 }),
 transport: new SSEClientTransport(new URL(mcp.url), {
   authProvider,
   requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
+  fetch: bunSafeFetch,
 }),
-const transport = new StreamableHTTPClientTransport(new URL(mcpConfig.url), { authProvider })
+const transport = new StreamableHTTPClientTransport(new URL(mcpConfig.url), {
+  authProvider,
+  fetch: bunSafeFetch,
+})

Affected MCP servers

Any MCP server with tool handlers that take longer than ~5 minutes to resolve. This is common for:

  • Interactive prompt tools where a human user is expected to type a response
  • Long-running CI/CD or build tools
  • Tools that wait for external approval workflows

Environment

  • OpenCode version: latest main
  • Bun version: 1.2.x
  • OS: macOS (arm64)
  • MCP SDK: @modelcontextprotocol/sdk (StreamableHTTPClientTransport)

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 9, 2026
  2. removed
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  3. rekram1-node commented on Jun 22, 2026

    @rekram1-node
    Collaborator

    this should be fixed due to the progress stuff

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

Metadata

Metadata

Assignees

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