thedotmack/claude-mem · error
cloud sync canonical payload: ${name} must decode to an obje
Error message
cloud sync canonical payload: ${name} must decode to an object What it means
The object-shaped counterpart for columns like metadata: the text parsed as JSON but decoded to an array, a primitive, or JSON 'null' — the contract requires a plain object. jsonPayloadColumn rejects it with the column name, blocking the push before a malformed op reaches the hub.
Source
Thrown at src/services/sync/CloudSync.ts:170
throw new Error(`cloud sync canonical payload: ${name} must be a non-negative safe integer`);
}
return String(value);
}
function jsonPayloadColumn(value: unknown, name: string, expected: 'array' | 'object'): unknown {
if (value === null || value === undefined) return null;
if (typeof value !== 'string') {
throw new Error(`cloud sync canonical payload: ${name} must be stored JSON text`);
}
let parsed: unknown;
try { parsed = JSON.parse(value); } catch {
throw new Error(`cloud sync canonical payload: ${name} is not valid JSON`);
}
if (expected === 'array' && !Array.isArray(parsed)) {
throw new Error(`cloud sync canonical payload: ${name} must decode to an array`);
}
if (expected === 'object' && (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))) {
throw new Error(`cloud sync canonical payload: ${name} must decode to an object`);
}
return parsed;
}
const KINDS: KindSpec[] = [
{
kind: 'observation',
localTable: 'observations',
selectSql: `
SELECT CAST(id AS TEXT) AS id, CAST(sync_rev AS TEXT) AS sync_rev,
memory_session_id, project, text, type, title, subtitle,
facts, narrative, concepts, files_read, files_modified, prompt_number,
discovery_tokens, content_hash, generated_by_model, agent_type, agent_id,
metadata, merged_into_project, created_at, created_at_epoch
FROM observations
WHERE synced_at IS NULL AND origin_device_id IS NULL
ORDER BY id LIMIT ${BATCH}`,
selectOneSql: `View on GitHub (pinned to e2d1df569a)
Solutions
- Find offenders: SELECT id FROM <table> WHERE metadata IS NOT NULL AND json_type(metadata) <> 'object'
- Repair: UPDATE <table> SET metadata = '{}' WHERE json_type(metadata) <> 'object' (or NULL for rows where metadata is genuinely absent)
- Re-run the sync push
- Align writers on a single metadata shape (plain object)
Example fix
-- before
SELECT id FROM observations WHERE metadata IS NOT NULL AND json_type(metadata) <> 'object';
-- after
UPDATE observations SET metadata = '{}'
WHERE metadata IS NOT NULL AND json_type(metadata) <> 'object'; Defensive patterns
Strategy: validation
Validate before calling
-- Pre-sync check: object-contract columns decode to objects SELECT COUNT(*) AS bad FROM observations WHERE metadata IS NOT NULL AND json_type(metadata) <> 'object'; -- bad = 0 before enabling cloud sync
Type guard
const decodesToObject = (v: unknown): v is string =>
typeof v === 'string' && (() => {
try { const p = JSON.parse(v); return typeof p === 'object' && p !== null && !Array.isArray(p); }
catch { return false; }
})(); Try / catch
try {
await cloudSync.push();
} catch (e) {
if (e instanceof Error && e.message.includes('must decode to an object')) {
// normalize metadata to '{}' or NULL, then re-push
} else throw e;
} Prevention
- Standardize metadata as a flat JSON object; never an array or scalar
- Do not store the literal text 'null' — write SQL NULL instead
- Assert json_type() in seeds and imports
- Document the metadata shape next to every writer
When it happens
Trigger: metadata stored as '[]' or a bare string/number; a writer storing null as the literal text 'null'; a migration that defaulted the column to an empty array instead of an empty object.
Common situations: Components disagreeing whether metadata is a map or a list; seed scripts writing placeholders; older builds encoding null differently.
Related errors
- cloud sync canonical payload: ${name} must decode to an arra
- cloud sync canonical payload: ${name} must be a non-negative
- cloud sync canonical payload: ${name} must be stored JSON te
- cloud sync canonical payload: ${name} is not valid JSON
- sync hub status: response is not JSON
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/edf7e472a809e0fb.
Report an issue: GitHub.