{"record":{"id":"e428b65d1a38d3ca","repo":"JuliusBrussee/caveman","slug":"cave-agent-tool-timeout-cave-network-error","errorCode":"cave_agent_tool_timeout | cave_network_error","errorMessage":"${timedOut ? `Caveman API request timed out after ${agentMcpTimeoutMS()}ms.` : error instanceof Error ? error.message : \"Caveman API request failed.\"}","messagePattern":"(.+?)ms\\.` : error instanceof Error \\? error\\.message : \"Caveman API request failed\\.\"\\}","errorType":"error_code","errorClass":"AgentMcpHTTPError","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":18394,"sourceCode":"    );\n  }\n  let response: Response;\n  try {\n    const request: RequestInit = {\n      method: options.method ?? \"GET\",\n      signal: AbortSignal.timeout(agentMcpTimeoutMS()),\n      headers: {\n        authorization: `Bearer ${cfg.token}`,\n        ...(options.method === \"POST\"\n          ? { \"content-type\": \"application/json\", \"x-cave-csrf\": \"cli\" }\n          : {}),\n      },\n    };\n    if (options.method === \"POST\") request.body = JSON.stringify(options.body ?? {});\n    response = await fetch(`${cfg.baseURL}${path}`, request);\n  } catch (error) {\n    const timedOut = error instanceof Error && error.name === \"TimeoutError\";\n    throw new AgentMcpHTTPError(\n      timedOut\n        ? `Caveman API request timed out after ${agentMcpTimeoutMS()}ms.`\n        : error instanceof Error\n          ? error.message\n          : \"Caveman API request failed.\",\n      timedOut ? \"cave_agent_tool_timeout\" : \"cave_network_error\",\n    );\n  }\n  const body = await response.json().catch(() => ({})) as JSONObject;\n  if (!response.ok) {\n    const envelope = body.error && typeof body.error === \"object\" && !Array.isArray(body.error)\n      ? body.error as Record<string, unknown>\n      : {};\n    const flatCode = typeof body.error === \"string\" ? body.error : undefined;\n    throw new AgentMcpHTTPError(\n      typeof envelope.message === \"string\"\n        ? envelope.message\n        : typeof body.message === \"string\"","sourceCodeStart":18376,"sourceCodeEnd":18412,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L18376-L18412","documentation":"This error is thrown by agentMcpRequest() when a fetch call to the Caveman API at cfg.baseURL fails at the transport level. If the failure is a TimeoutError the code is cave_agent_tool_timeout and the message reports the configured timeout from agentMcpTimeoutMS(); any other network failure (DNS, refused connection, TLS, socket reset) yields cave_network_error with the underlying error message. It wraps every non-HTTP-level failure of the Caveman agent MCP HTTP client.","triggerScenarios":"fetch() rejects before a response is received: request exceeded agentMcpTimeoutMS() abort (name === 'TimeoutError'), gateway unreachable/Connection refused, DNS failure, TLS handshake failure, or the socket reset mid-request on any POST/GET to `${cfg.baseURL}${path}`.","commonSituations":"Caveman gateway not running or wrong baseURL configured, CAVEMAN agent tools called from a CLI session whose gateway has crashed, corporate proxy/firewall blocking the endpoint, slow backend exceeding the MCP tool timeout, or typo'd port in the gateway URL.","solutions":["Verify the Caveman gateway is running and reachable: curl ${baseURL} from the same host.","Check the baseURL/gateway URL configuration (env vars like CAVEMAN_LISTEN / gateway URL) for typos in host or port.","If timeouts, raise the MCP tool timeout via agentMcpTimeoutMS() configuration or investigate slow backend endpoints.","Check network/proxy/firewall settings; ensure the gateway port is not blocked.","Retry the tool call if the failure was transient (e.g. restarting gateway)."],"exampleFix":"// before (gateway down)\nconst res = await agentMcpRequest(\"POST\", \"/v1/agent/run\", body); // throws cave_network_error\n// after\ngatewayHostPort check / start gateway:\n//   caveman gateway start\nconst res = await agentMcpRequest(\"POST\", \"/v1/agent/run\", body);","handlingStrategy":"try-catch","validationCode":"// preflight: check gateway reachability before issuing agent tool calls\nconst { host, port } = gatewayHostPort();\nconst reachable = await fetch(`http://${host}:${port}/`, { signal: AbortSignal.timeout(2000) })\n  .then(r => r.ok).catch(() => false);\nif (!reachable) throw new Error(`Caveman gateway unreachable at ${host}:${port}; start it first`);","typeGuard":"function isCavemanNetworkError(e: unknown): e is { name: \"AgentMcpHTTPError\"; code: string; message: string } {\n  return e instanceof Error && (e as { code?: string }).code === \"cave_network_error\"\n    || (e instanceof Error && e.name === \"TimeoutError\");\n}","tryCatchPattern":"try {\n  await agentMcpRequest(\"POST\", path, body);\n} catch (e) {\n  if (e instanceof Error && e.name === \"TimeoutError\") {\n    // retry with backoff or raise timeout\n  } else if (isCavemanNetworkError(e)) {\n    console.error(\"Gateway unreachable:\", e.message); // check gateway is running\n  } else throw e;\n}","preventionTips":["Start/health-check the Caveman gateway before invoking agent tools","Validate baseURL/gateway host:port config at startup","Set the MCP tool timeout (agentMcpTimeoutMS) to a realistic value for your workload","Monitor network/proxy changes in CI environments"],"tags":["network","timeout","http"],"backgroundTag":"request-timeout","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}