JuliusBrussee/caveman · error · AgentMcpHTTPError

cave_agent_tool_timeout | cave_network_error

cave_agent_tool_timeout | cave_network_error

Error message

${timedOut ? `Caveman API request timed out after ${agentMcpTimeoutMS()}ms.` : error instanceof Error ? error.message : "Caveman API request failed."}

What it means

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.

Solutions

  1. Verify the Caveman gateway is running and reachable: curl ${baseURL} from the same host.
  2. Check the baseURL/gateway URL configuration (env vars like CAVEMAN_LISTEN / gateway URL) for typos in host or port.
  3. If timeouts, raise the MCP tool timeout via agentMcpTimeoutMS() configuration or investigate slow backend endpoints.
  4. Check network/proxy/firewall settings; ensure the gateway port is not blocked.
  5. Retry the tool call if the failure was transient (e.g. restarting gateway).

Example fix

// before (gateway down)
const res = await agentMcpRequest("POST", "/v1/agent/run", body); // throws cave_network_error
// after
gatewayHostPort check / start gateway:
//   caveman gateway start
const res = await agentMcpRequest("POST", "/v1/agent/run", body);
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: check gateway reachability before issuing agent tool calls
const { host, port } = gatewayHostPort();
const reachable = await fetch(`http://${host}:${port}/`, { signal: AbortSignal.timeout(2000) })
  .then(r => r.ok).catch(() => false);
if (!reachable) throw new Error(`Caveman gateway unreachable at ${host}:${port}; start it first`);

Type guard

function isCavemanNetworkError(e: unknown): e is { name: "AgentMcpHTTPError"; code: string; message: string } {
  return e instanceof Error && (e as { code?: string }).code === "cave_network_error"
    || (e instanceof Error && e.name === "TimeoutError");
}

Try / catch

try {
  await agentMcpRequest("POST", path, body);
} catch (e) {
  if (e instanceof Error && e.name === "TimeoutError") {
    // retry with backoff or raise timeout
  } else if (isCavemanNetworkError(e)) {
    console.error("Gateway unreachable:", e.message); // check gateway is running
  } else throw e;
}

Prevention

When it happens

Trigger: 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}`.

Common situations: 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.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/e428b65d1a38d3ca. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:18394

    );
  }
  let response: Response;
  try {
    const request: RequestInit = {
      method: options.method ?? "GET",
      signal: AbortSignal.timeout(agentMcpTimeoutMS()),
      headers: {
        authorization: `Bearer ${cfg.token}`,
        ...(options.method === "POST"
          ? { "content-type": "application/json", "x-cave-csrf": "cli" }
          : {}),
      },
    };
    if (options.method === "POST") request.body = JSON.stringify(options.body ?? {});
    response = await fetch(`${cfg.baseURL}${path}`, request);
  } catch (error) {
    const timedOut = error instanceof Error && error.name === "TimeoutError";
    throw new AgentMcpHTTPError(
      timedOut
        ? `Caveman API request timed out after ${agentMcpTimeoutMS()}ms.`
        : error instanceof Error
          ? error.message
          : "Caveman API request failed.",
      timedOut ? "cave_agent_tool_timeout" : "cave_network_error",
    );
  }
  const body = await response.json().catch(() => ({})) as JSONObject;
  if (!response.ok) {
    const envelope = body.error && typeof body.error === "object" && !Array.isArray(body.error)
      ? body.error as Record<string, unknown>
      : {};
    const flatCode = typeof body.error === "string" ? body.error : undefined;
    throw new AgentMcpHTTPError(
      typeof envelope.message === "string"
        ? envelope.message
        : typeof body.message === "string"

View on GitHub (pinned to 3ee70a1026)