different-ai/openwork · error · ProbeFailure

Connection closed before the tool response completed

Error message

Connection closed before the tool response completed

What it means

The tools/call POST is wrapped so that any transport failure before a response arrives is converted into a deterministic probe failure. For mutation tools the probe reports PROVIDER_EXECUTION / mutation_indeterminate (the commit may or may not have happened); for everything else it reports MCP_TOOL_EXECUTION / mcp_tool with this message.

Source

Thrown at packages/enterprise-mcp-mock-server/src/testing/probe.ts:886

            const mutationRequired = fault?.effect === "commit-then-disconnect"
            const tool = discoveredTools.find((candidate) => candidate.kind === (mutationRequired ? "mutation" : "read"))
            if (!tool) throw new ProbeFailure("MCP_TOOL_EXECUTION", "mcp_tool", "No suitable tool was available for the requested probe")
            return { name: tool.name, arguments: defaultArguments(tool.inputSchema) }
          })()
      const selectedTool = profile.tools.find((tool) => tool.name === selected.name)
      if (mode === "safe-read" && selectedTool?.kind !== "read") {
        throw new ProbeFailure("CONFIGURATION", "configuration", "safe-read mode accepts only a declared read-only tool")
      }
      startedAt = Date.now()
      let toolResponse: Response
      try {
        toolResponse = await fetchStep(mcpUrl, {
          method: "POST",
          headers: sessionHeaders,
          body: JSON.stringify({ jsonrpc: "2.0", id: 100, method: "tools/call", params: selected }),
        }, "MCP_TOOL_EXECUTION", overallDeadline)
      } catch {
        throw new ProbeFailure(
          selectedTool?.kind === "mutation" ? "PROVIDER_EXECUTION" : "MCP_TOOL_EXECUTION",
          selectedTool?.kind === "mutation" ? "mutation_indeterminate" : "mcp_tool",
          "Connection closed before the tool response completed",
        )
      }
      const envelope = await parseRpc(await expectOk(toolResponse, "MCP_TOOL_EXECUTION"), "MCP_TOOL_EXECUTION")
      if (envelope.id !== 100) {
        throw new ProbeFailure("MCP_TOOL_EXECUTION", "mcp_tool", "tools/call response JSON-RPC id did not match the request")
      }
      if (envelope.error) throw new ProbeFailure("MCP_TOOL_EXECUTION", "mcp_tool", envelope.error.message)
      const toolResult = parseAt(
        z.object({ isError: z.boolean(), structuredContent: z.unknown().optional() }),
        envelope.result,
        "MCP_TOOL_EXECUTION",
        "mcp_tool",
        "Tool response did not match the MCP result shape",
      )
      if (toolResult.isError) {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. If testing the commit-then-disconnect fault, expect mutation_indeterminate and assert the downstream commit/idempotency behavior instead of treating it as a bug.
  2. Verify the mock server is running and mcpUrl/sessionHeaders are correct and current.
  3. Increase overallDeadline if the tool legitimately takes longer than the budget.
  4. Make the mutation tool idempotent so a retry after an indeterminate failure is safe.
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling the tool, confirm the endpoint is reachable:
const ping = await fetch(mcpUrl, { method: "POST", headers: sessionHeaders, body: JSON.stringify({ jsonrpc: "2.0", id: 0, method: "ping" }) });
if (!ping.ok) throw new Error(`MCP endpoint unreachable: ${ping.status}`);

Try / catch

try {
  await probeEnterpriseMcpMockServer(options);
} catch (e) {
  if (e instanceof ProbeFailure && e.reason === "mutation_indeterminate") {
    // check downstream side effects before deciding pass/fail; do not auto-retry mutations
  } else if (e instanceof ProbeFailure && e.reason === "mcp_tool" && e.message.includes("Connection closed")) {
    // verify server health and deadline, then retry once for read-only calls
  } else throw e;
}

Prevention

When it happens

Trigger: The fetch of POST tools/call (id 100) with sessionHeaders throws or times out against the overallDeadline — connection reset, server crashed mid-call (e.g. commit-then-disconnect fault), or deadline exceeded before headers arrive.

Common situations: Mock server deliberately disconnects after committing (commit-then-disconnect fault scenario); server not listening on mcpUrl; network proxy dropping keep-alive connections; overallDeadline too tight for a slow tool.

Understand the failure class

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/818339056bc93a45. Report an issue: GitHub.