{"record":{"id":"1148f14734eb3193","repo":"thedotmack/claude-mem","slug":"cloud-sync-canonical-payload-name-is-not-valid","errorCode":null,"errorMessage":"cloud sync canonical payload: ${name} is not valid JSON","messagePattern":"cloud sync canonical payload: (.+?) is not valid JSON","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/sync/CloudSync.ts","lineNumber":164,"sourceCode":"function decimalPayload(value: unknown, name: string, nullable = false): string | null {\n  if (value === null || value === undefined) {\n    if (nullable) return null;\n    return '0';\n  }\n  if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {\n    throw new Error(`cloud sync canonical payload: ${name} must be a non-negative safe integer`);\n  }\n  return String(value);\n}\n\nfunction jsonPayloadColumn(value: unknown, name: string, expected: 'array' | 'object'): unknown {\n  if (value === null || value === undefined) return null;\n  if (typeof value !== 'string') {\n    throw new Error(`cloud sync canonical payload: ${name} must be stored JSON text`);\n  }\n  let parsed: unknown;\n  try { parsed = JSON.parse(value); } catch {\n    throw new Error(`cloud sync canonical payload: ${name} is not valid JSON`);\n  }\n  if (expected === 'array' && !Array.isArray(parsed)) {\n    throw new Error(`cloud sync canonical payload: ${name} must decode to an array`);\n  }\n  if (expected === 'object' && (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))) {\n    throw new Error(`cloud sync canonical payload: ${name} must decode to an object`);\n  }\n  return parsed;\n}\n\nconst KINDS: KindSpec[] = [\n  {\n    kind: 'observation',\n    localTable: 'observations',\n    selectSql: `\n      SELECT CAST(id AS TEXT) AS id, CAST(sync_rev AS TEXT) AS sync_rev,\n        memory_session_id, project, text, type, title, subtitle,\n        facts, narrative, concepts, files_read, files_modified, prompt_number,","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/sync/CloudSync.ts#L146-L182","documentation":"The column is stored as text, but JSON.parse rejects it — the stored value is truncated or otherwise not valid JSON (a crash mid-write, mangled quotes, or plain prose in a JSON column). The push halts at this row so the corrupt payload never reaches the hub; the message names the offending column.","triggerScenarios":"Power loss or crash during the UPDATE that wrote the column leaving truncated text; the SQLite file copied while hot; sed/regex edits mangling escaped quotes; a writer appending to the column instead of replacing it.","commonSituations":"Unclean shutdown during heavy writes; DB restored from a partial backup; manual data surgery on the file; encoding conversion tools corrupting escape sequences.","solutions":["Find the invalid rows with SQLite's own validator: SELECT id FROM <table> WHERE <col> IS NOT NULL AND json_valid(<col>) = 0","Repair or null them out per semantics: UPDATE <table> SET <col> = '[]' WHERE <col> IS NOT NULL AND json_valid(<col>) = 0 (use NULL if the column is nullable/optional)","Re-run the sync push","Investigate what wrote the bad value — a recurring truncation pattern points to a crash loop or hot-copy backup"],"exampleFix":"-- before\nSELECT id, facts FROM observations WHERE facts IS NOT NULL AND json_valid(facts) = 0;\n-- after\nUPDATE observations SET facts = '[]'\nWHERE facts IS NOT NULL AND json_valid(facts) = 0;","handlingStrategy":"validation","validationCode":"-- Pre-sync check: stored JSON actually parses\nSELECT COUNT(*) AS bad FROM observations\n WHERE (facts IS NOT NULL AND json_valid(facts) = 0)\n    OR (concepts IS NOT NULL AND json_valid(concepts) = 0)\n    OR (metadata IS NOT NULL AND json_valid(metadata) = 0);\n-- bad = 0 before enabling cloud sync","typeGuard":"const isParsableJsonText = (v: unknown): v is string =>\n  typeof v === 'string' && (() => { try { JSON.parse(v); return true; } catch { return false; } })();","tryCatchPattern":"try {\n  await cloudSync.push();\n} catch (e) {\n  if (e instanceof Error && e.message.includes('is not valid JSON')) {\n    // repair with UPDATE ... SET col = '[]' WHERE json_valid(col) = 0, then re-push\n  } else throw e;\n}","preventionTips":["Never hot-copy the SQLite file while writers are active — use the backup API or checkpoint first","Add json_valid() assertions in test fixtures and seed data","Avoid sed/regex edits on serialized JSON columns","Investigate crash loops if truncation recurs"],"tags":["cloud-sync","json","corrupt-data","sqlite","validation"],"backgroundTag":"invalid-json","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}