{"record":{"id":"7c01b7e4f5a5166f","repo":"JuliusBrussee/caveman","slug":"cave-request-failed-response-status","errorCode":null,"errorMessage":"cave request failed (${response.status})","messagePattern":"cave request failed \\((.+?)\\)","errorType":"http","errorClass":"CaveRequestError","httpStatus":null,"severity":"error","filePath":"packages/sdk/typescript/src/index.ts","lineNumber":2123,"sourceCode":"}\n\nexport class CaveRequestError extends Error {\n  constructor(readonly status: number, readonly path: string, message: string) {\n    super(message);\n    this.name = \"CaveRequestError\";\n  }\n}\n\nasync function request(cave: Cave, path: string, body?: unknown, workflow?: string, trace?: TraceContext, extraHeaders?: Record<string, string>): Promise<Record<string, unknown>> {\n  const init: RequestInit = {\n    method: body === undefined ? \"GET\" : \"POST\",\n    headers: { ...headers(cave, workflow ?? cave.options.defaultWorkflow ?? \"unlabeled-workflow\", undefined, undefined, trace), ...extraHeaders }\n  };\n  if (body !== undefined) init.body = JSON.stringify(body);\n  const response = await caveFetch(cave, `${cave.options.baseURL}${path}`, init);\n  if (!response.ok) {\n    discardResponse(response);\n    throw new CaveRequestError(response.status, path, `cave request failed (${response.status})`);\n  }\n  let decoded: unknown;\n  try {\n    if (typeof response.text === \"function\") {\n      decoded = JSON.parse(await response.text());\n    } else if (typeof response.json === \"function\") {\n      decoded = await response.json();\n    } else {\n      throw new Error(\"missing response decoder\");\n    }\n  } catch {\n    throw new CaveRequestError(response.status, path, \"cave response was not valid JSON\");\n  }\n  if (decoded === null || typeof decoded !== \"object\" || Array.isArray(decoded)) {\n    throw new CaveRequestError(response.status, path, \"cave response must be a JSON object\");\n  }\n  return decoded as Record<string, unknown>;\n}","sourceCodeStart":2105,"sourceCodeEnd":2141,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/typescript/src/index.ts#L2105-L2141","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Log err.status and err.path (CaveRequestError fields) to identify which endpoint and HTTP status failed","Check Cave service health and that cave.options.baseURL points at the correct environment","Verify API credentials/keys are valid and not expired (401/403 statuses)","For 5xx statuses, retry with backoff; for 429, honor rate limits before retrying"],"exampleFix":"// before\nconst result = await sdk.postLabel(input); // throws unhandled CaveRequestError\n// after\ntry {\n  const result = await sdk.postLabel(input);\n} catch (err) {\n  if (err instanceof CaveRequestError) {\n    console.error(`cave ${err.path} failed with ${err.status}`);\n    if (err.status >= 500 || err.status === 429) return retryWithBackoff();\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"if (!cave.options.baseURL.startsWith('http')) throw new Error('baseURL must be an absolute URL');","typeGuard":"function isCaveRequestError(e: unknown): e is CaveRequestError { return e instanceof CaveRequestError; }","tryCatchPattern":"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; }","preventionTips":["Always catch CaveRequestError around SDK calls rather than generic Error","Branch on err.status before deciding to retry (5xx/429 retry, 4xx fix request)","Monitor baseURL configuration per environment (dev/staging/prod)","Keep cave credentials rotation handled with a refresh path"],"tags":["http","network","api","sdk"],"backgroundTag":"http-error-response","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"}