thedotmack/claude-mem · error
sync hub push: malformed acked[${index}]
Error message
sync hub push: malformed acked[${index}] What it means
Thrown when an acked entry IS a plain object but one or more required fields have the wrong type. Required: `id`, `kind`, `entity_rev`, `operation_sha256`, `seq` must be strings; `origin_local_id` must be either null or a string. This is the deeper field-level shape check that runs after [120] passes and before the canonical-decimal assertions on entity_rev/seq/origin_local_id.
Source
Thrown at src/services/sync/CloudSync.ts:1001
if (typeof headSeq !== 'string' || typeof projectedSeq !== 'string') {
throw new Error('sync hub push: response requires decimal-string head_seq/projected_seq');
}
assertCanonicalDecimal(headSeq);
assertCanonicalDecimal(projectedSeq);
const validatedAcked = acked.map((value, index): AckedOp => {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`sync hub push: acked[${index}] must be an object`);
}
const item = value as Record<string, unknown>;
if (
typeof item.id !== 'string'
|| typeof item.kind !== 'string'
|| (item.origin_local_id !== null && typeof item.origin_local_id !== 'string')
|| typeof item.entity_rev !== 'string'
|| typeof item.operation_sha256 !== 'string'
|| typeof item.seq !== 'string'
) {
throw new Error(`sync hub push: malformed acked[${index}]`);
}
assertCanonicalDecimal(item.entity_rev, { positive: true });
assertCanonicalDecimal(item.seq, { positive: true });
if (typeof item.origin_local_id === 'string') assertCanonicalDecimal(item.origin_local_id);
return item as unknown as AckedOp;
});
return {
acked: validatedAcked,
head_seq: headSeq,
projected_seq: projectedSeq,
};
}
/**
* Treat a successful push response as one atomic acknowledgment proof.
* Nothing in this method mutates SQLite. stampAcked() runs only after every
* tuple, multiplicity, sequence, and checkpoint invariant has passed.
*/View on GitHub (pinned to d768ba3643)
Solutions
- Read the acked[index] object from the hub response and verify each of id/kind/entity_rev/operation_sha256/seq is a string and origin_local_id is null-or-string.
- If the hub is emitting numeric rev/seq, fix the hub to emit canonical decimal strings (e.g. "5" not 5) — the assertCanonicalDecimal calls immediately after this check require strings.
- Align hub and client versions so the AckedOp wire schema matches.
- Replay the failing push with response logging to capture the exact offending field and index.
Example fix
// before: { "entity_rev": 5, "seq": 12, "origin_local_id": 0, ... }
// after: { "entity_rev": "5", "seq": "12", "origin_local_id": null, ... } Defensive patterns
Strategy: type-guard
Type guard
function isAckedOp(v: unknown): v is AckedOp {
if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;
const o = v as Record<string, unknown>;
return typeof o.id === 'string'
&& typeof o.kind === 'string'
&& (o.origin_local_id === null || typeof o.origin_local_id === 'string')
&& typeof o.entity_rev === 'string'
&& typeof o.operation_sha256 === 'string'
&& typeof o.seq === 'string';
} Try / catch
try { await cloudSync.push(pushed); }
catch (e) {
if (e instanceof Error && e.message.includes('malformed acked')) {
// hub field-type regression; surface but leave batch un-stamped
logger.error('SYNC', e.message); return;
}
throw e;
} Prevention
- Require the hub to emit rev/seq as canonical decimal strings, not JSON numbers.
- Add an integration test that asserts every hub ack field type against AckedOp.
- Keep the assertCanonicalDecimal positive-option behavior aligned between client and hub tests.
When it happens
Trigger: A 200 push response whose acked object has a non-string field, e.g. `entity_rev` as a number, `origin_local_id` as `0`, or `seq` missing/undefined. Fires per-index inside `acked.map`, so the index in the message identifies which entry is malformed.
Common situations: Hub serializes numeric rev/seq fields as JSON numbers instead of decimal strings (the contract requires canonical decimal strings), `origin_local_id` emitted as `0`/empty rather than null, or a field renamed/omitted after a hub schema change. Also from a hand-crafted mock response missing a field.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- sync hub push: acked[${index}] must be an object
- sync hub push: 200 response contains an extra or mismatched
- sync hub push: duplicate operation tuple claimed different s
- sync hub push: distinct operation tuples claimed the same se
- sync hub push: 200 response acknowledgment multiplicity mism
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/44f9374f94e4e9ab.
Report an issue: GitHub.