thedotmack/claude-mem · error
sync hub push: acked
Error message
sync hub push: acked[${index}] must be an object What it means
Inside the push response's acked array, element at the failing index is not an object — it is null, an array, or a JSON primitive. Each ack must be an object with id/kind/origin_local_id/entity_rev/operation_sha256/seq; validation stops before stamping, so no local rows change and flush() retries with backoff.
Solutions
- Log/inspect the raw acked array from a curl push to see what element type the hub actually returns
- Fix the hub to emit one object per acknowledged op with all six required fields
- If nulls represent 'not acked', remove them server-side — the acked array must contain only acknowledgments
- Re-deploy hub and client together once the shape matches
Defensive patterns
Strategy: type-guard
Validate before calling
// Validate every ack entry's basic shape (hub tests / custom clients):
function ackEntriesAreObjects(acked: unknown[]): boolean {
return acked.every(a => !!a && typeof a === 'object' && !Array.isArray(a));
} Type guard
function isAckedOpShape(v: unknown): v is Record<string, unknown> {
return !!v && typeof v === 'object' && !Array.isArray(v);
} Try / catch
if (/acked\[\d+\] must be an object/.test(sync.status().lastError ?? '')) {
// dump the raw acked array from the hub; nulls/scalars indicate a hub mapping bug
} Prevention
- Hub: emit ack objects only for acknowledged ops — never null placeholders
- Keep hub ack construction typed (a struct/interface, not ad-hoc JSON)
- Include mixed ack arrays in hub fixtures and contract tests
When it happens
Trigger: Hub emits [null, ...] for skipped ops; a hub bug mapping the wrong list into acked (e.g. seq strings instead of objects); a proxy truncating/reordering JSON; stub fixtures with scalar entries.
Common situations: Hub refactor changed the ack item shape; partial hub deployment serving mixed versions; hand-written mock hub in integration tests.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- sync hub push: malformed acked
- sync hub push: response missing acked array
- sync hub push: 200 response acknowledgment multiplicity…
- sync hub push: 200 response contains an extra or mismatched…
- sync hub push: acknowledgment seq exceeds head_seq
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/98ddc9679fc72f0c.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:990
try {
parsed = await res.json();
} catch {
throw new Error('sync hub push: response is not JSON');
}
const acked = (parsed as { acked?: unknown } | null)?.acked;
if (!Array.isArray(acked)) {
throw new Error('sync hub push: response missing acked array');
}
const headSeq = (parsed as { head_seq?: unknown }).head_seq;
const projectedSeq = (parsed as { projected_seq?: unknown }).projected_seq;
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 {View on GitHub (pinned to e2d1df569a)