{"record":{"id":"94bef159b6aa1488","repo":"JuliusBrussee/caveman","slug":"eval-evidence-import-failed-response-status","errorCode":null,"errorMessage":"eval evidence import failed (${response.status}): ${JSON.stringify(body)}","messagePattern":"eval evidence import failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":17181,"sourceCode":"  if (items.length === 0) throw new Error(\"eval evidence file contains no records\");\n\n  const cfg = await config();\n  const project = flagFrom(argv, \"--project\", cfg.projectId ?? \"\");\n  const response = await fetch(`${cfg.baseURL}/api/v1/eval-evidence/batches`, {\n    method: \"POST\",\n    headers: {\n      authorization: `Bearer ${cfg.token}`,\n      \"content-type\": \"application/json\",\n      \"x-cave-csrf\": \"cli\",\n    },\n    body: JSON.stringify({\n      ...(project ? { project_id: project } : {}),\n      dry_run: argv.includes(\"--dry-run\"),\n      items,\n    }),\n  });\n  const body = await response.json();\n  if (!response.ok) throw new Error(`eval evidence import failed (${response.status}): ${JSON.stringify(body)}`);\n  print(body);\n}\n\n// ---------------------------------------------------------------------------\n// Signed usage receipts: air-gapped export + offline verification.\n//\n// A receipt is a per-(org,project,day) aggregate, Ed25519-signed and chained by\n// prev_receipt_hash. `verify` recomputes each canonical hash, checks the\n// signature against the published public key, and walks the chain — all offline,\n// so finance (either party) can re-derive trust without contacting Caveman. It is\n// the cross-language counterpart of cloud/metering's VerifyChain.\n// ---------------------------------------------------------------------------\n\ntype ReceiptSignature = { alg: string; key_id: string; sig: string };\ntype ReceiptScope = { org_hash: string; project_hash: string };\ntype ReceiptOptimizer = { optimizer_id_hash: string; requests_optimized: number };\ntype Receipt = {\n  schema: string;","sourceCodeStart":17163,"sourceCodeEnd":17199,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/5184b3d11ac6a1acb7d44b9bfaa31698157cff97/packages/cli/src/index.ts#L17163-L17199","documentation":"The eval-evidence batch POST to /api/v1/eval-evidence/batches returned a non-2xx status. The CLI surfaces the HTTP status code and the parsed response body verbatim, so the server's own error message is always visible. Auth, CSRF, project-scope, and per-record validation failures all surface through this single throw.","triggerScenarios":"401/403 from an expired or wrong bearer token; 400 when the server rejects record shapes or the project_id; 404 for an unknown project uuid; 413/422 when server-side caps trip; an intermediary proxy replacing the body.","commonSituations":"Token expiring between login and import in long CI runs; typo'd --project uuid; server updated with stricter validation than the CLI expects; corporate proxies mangling responses.","solutions":["Read the embedded body — it carries the server's actual reason","On 401/403: run `caveman login` again and retry the import","On 400/404: fix the project uuid or the record fields named in the body","For persistent failures: compare CLI and server versions for validation drift"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-flight auth and project scope before the big upload.\nconst me = await fetch(`${cfg.baseURL}/api/v1/me`, { headers: { authorization: `Bearer ${cfg.token}` } });\nif (!me.ok) throw new Error('token invalid — re-login before import');","typeGuard":null,"tryCatchPattern":"try {\n  await postBatch(items);\n} catch (e) {\n  const msg = (e as Error).message;\n  const status = Number(/\\((\\d+)\\)/.exec(msg)?.[1] ?? 0);\n  if (status === 401) {\n    await relogin();\n    await postBatch(items); // idempotent retry once auth is refreshed\n  } else throw e;\n}","preventionTips":["Re-login immediately before long-running CI imports","Validate the project uuid format (and its existence) before upload","Log the response body verbatim — it is the server's diagnosis"],"tags":["http","api-error","eval","import","auth"],"backgroundTag":"http-error-response","analyzedSha":"5184b3d11ac6a1acb7d44b9bfaa31698157cff97","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}