{"record":{"id":"641c55ced390796e","repo":"JuliusBrussee/caveman","slug":"result-error-message-sync-import-failed-response-status","errorCode":null,"errorMessage":"result.error?.message ?? `sync import failed (${response.status})`","messagePattern":"result\\.error\\?\\.message \\?\\? `sync import failed \\((.+?)\\)`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":10375,"sourceCode":"    const ts = typeof r.ts === \"string\" ? r.ts : \"\";\n    // The proxy writes ClickHouse-layout UTC timestamps (\"YYYY-MM-DD HH:MM:SS.mmm\").\n    const ms = Date.parse(ts.includes(\"T\") ? ts : ts.replace(\" \", \"T\") + \"Z\");\n    if (!Number.isFinite(ms)) return [];\n    return directiveInstalls.filter((d) => ms >= d.installedAtMs).map((d) => d.id);\n  };\n  const body = rows.map((r) => syncRequestSpan(r, rowDirectives(r))).join(\"\\n\") + \"\\n\";\n  const response = await fetch(`${cfg.baseURL}/api/v1/imports?format=caveman-jsonl`, {\n    method: \"POST\",\n    headers: {\n      authorization: `Bearer ${cfg.token}`,\n      \"content-type\": \"application/octet-stream\",\n      \"x-cave-csrf\": \"cli\",\n    },\n    body,\n  });\n  const result = (await response.json().catch(() => ({}))) as { status?: string; error?: { message?: string } };\n  if (!response.ok || result.status !== \"completed\") {\n    throw new Error(result.error?.message ?? `sync import failed (${response.status})`);\n  }\n\n  const num = (v: unknown) => (typeof v === \"bigint\" ? Number(v) : typeof v === \"number\" && Number.isFinite(v) ? v : 0);\n  const maxId = rows.reduce((m, r) => Math.max(m, num(r.id)), since);\n  writeSyncWatermark(key, maxId);\n  const tokensSaved = rows.reduce((sum, r) => sum + Math.max(0, num(r.compression_tokens_before) - num(r.compression_tokens_after)), 0);\n  const tokenBases = new Set(rows.map((r) => (typeof r.compression_token_count_basis === \"string\" ? r.compression_token_count_basis : \"\")).filter(Boolean));\n  const tokenCountBasis = tokenBases.size === 0 ? \"unavailable\" : tokenBases.size === 1 ? [...tokenBases][0] ?? \"unavailable\" : \"mixed\";\n  const cachedInputTokens = rows.reduce((sum, r) => sum + Math.max(0, num(r.cached_input_tokens)), 0);\n  const cacheCreationInputTokens = rows.reduce((sum, r) => sum + Math.max(0, num(r.cache_creation_input_tokens)), 0);\n  const headlineCompressionRefused = cacheCreationInputTokens > cachedInputTokens;\n  const savingsUSD = rows.reduce((sum, r) => sum + num(r.savings_usd), 0);\n  return {\n    kind: \"synced\",\n    spans: rows.length,\n    tokensSaved,\n    tokenCountBasis,\n    cachedInputTokens,","sourceCodeStart":10357,"sourceCodeEnd":10393,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L10357-L10393","documentation":"Thrown when the sync import POST completes but the server does not report a completed import: either `response.ok` is false or the parsed body's `status` is not \"completed\". If the server supplied `error.message`, that message is surfaced instead of the generic HTTP-status fallback.","triggerScenarios":"`caveman sync` when the import endpoint rejects the batch (validation, auth, 5xx) or returns a body with status other than \"completed\" (e.g. \"partial\", \"failed\").","commonSituations":"Spend-store rows the server refuses (schema drift between CLI and gateway); expired access token (401); gateway returning partial-completed statuses; proxy interference mangling the response.","solutions":["Read the surfaced `error.message` for the server-side rejection reason.","For 401 responses, re-login (`caveman login`) and retry the sync.","Upgrade/align CLI and gateway versions so the import payload and status contract match.","The sync watermark is only advanced after success, so simply retrying after fixing the cause re-sends the same rows safely."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm auth and payload shape before syncing\nconst res = await fetch(`${baseURL}/healthz`);\nif (!res.ok) throw new Error(\"gateway unhealthy — sync would fail\");","typeGuard":"function isCompletedImport(v: unknown): v is { status: \"completed\" } {\n  return (v as { status?: unknown })?.status === \"completed\";\n}","tryCatchPattern":"try {\n  await syncImport();\n} catch (e) {\n  console.error(`Sync import failed: ${e.message}`); // server error.message takes precedence\n  // watermark is not advanced on failure, so a plain retry is safe\n  await sleep(5000);\n  await syncImport();\n}","preventionTips":["Re-login when the access token may have expired (401 is a common cause).","Keep CLI/gateway payload schema aligned before bulk imports.","Rely on the watermark: failed syncs re-send the same rows on retry, so just retry."],"tags":["sync","http","api","import"],"backgroundTag":"api-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"}