JuliusBrussee/caveman · error · CaveRequestError

cave request failed ( )

Error message

cave request failed (${response.status})

What it means

This is the generic non-2xx response error for SDK calls routed through the shared caveFetch helper. When the Cave server answers any /sdk/v1/* request with an HTTP status outside 200-299, the body is discarded and a CaveRequestError carrying the numeric status and request path is thrown. It is the library's way of surfacing server-side failures (auth rejection, not found, server error) as a typed error instead of leaking the raw fetch Response.

Solutions

  1. Log err.status and err.path (CaveRequestError fields) to identify which endpoint and HTTP status failed
  2. Check Cave service health and that cave.options.baseURL points at the correct environment
  3. Verify API credentials/keys are valid and not expired (401/403 statuses)
  4. For 5xx statuses, retry with backoff; for 429, honor rate limits before retrying

Example fix

// before
const result = await sdk.postLabel(input); // throws unhandled CaveRequestError
// after
try {
  const result = await sdk.postLabel(input);
} catch (err) {
  if (err instanceof CaveRequestError) {
    console.error(`cave ${err.path} failed with ${err.status}`);
    if (err.status >= 500 || err.status === 429) return retryWithBackoff();
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!cave.options.baseURL.startsWith('http')) throw new Error('baseURL must be an absolute URL');

Type guard

function isCaveRequestError(e: unknown): e is CaveRequestError { return e instanceof CaveRequestError; }

Try / catch

try { const r = await sdk.someCall(); } catch (e) { if (e instanceof CaveRequestError) { if (e.status >= 500 || e.status === 429) return retry(); if (e.status === 401 || e.status === 403) return refreshCredentials(); throw e; } throw e; }

Prevention

When it happens

Trigger: Any SDK method that performs a JSON-object request (e.g. artifact list/post, label operations) where the Cave service returns 400/401/404/429/5xx. Specific triggers: expired or wrong cave key (401), unknown artifact or workflow path (404), server-side handler crash (500), rate limiting (429).

Common situations: Deployed Cave service misconfigured or down; API base URL pointing at the wrong environment (staging vs prod); auth credentials rotated while the client is still running; calling a path that does not exist in the deployed server version.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at packages/sdk/typescript/src/index.ts:2123

}

export class CaveRequestError extends Error {
  constructor(readonly status: number, readonly path: string, message: string) {
    super(message);
    this.name = "CaveRequestError";
  }
}

async function request(cave: Cave, path: string, body?: unknown, workflow?: string, trace?: TraceContext, extraHeaders?: Record<string, string>): Promise<Record<string, unknown>> {
  const init: RequestInit = {
    method: body === undefined ? "GET" : "POST",
    headers: { ...headers(cave, workflow ?? cave.options.defaultWorkflow ?? "unlabeled-workflow", undefined, undefined, trace), ...extraHeaders }
  };
  if (body !== undefined) init.body = JSON.stringify(body);
  const response = await caveFetch(cave, `${cave.options.baseURL}${path}`, init);
  if (!response.ok) {
    discardResponse(response);
    throw new CaveRequestError(response.status, path, `cave request failed (${response.status})`);
  }
  let decoded: unknown;
  try {
    if (typeof response.text === "function") {
      decoded = JSON.parse(await response.text());
    } else if (typeof response.json === "function") {
      decoded = await response.json();
    } else {
      throw new Error("missing response decoder");
    }
  } catch {
    throw new CaveRequestError(response.status, path, "cave response was not valid JSON");
  }
  if (decoded === null || typeof decoded !== "object" || Array.isArray(decoded)) {
    throw new CaveRequestError(response.status, path, "cave response must be a JSON object");
  }
  return decoded as Record<string, unknown>;
}

View on GitHub (pinned to 3ee70a1026)