thedotmack/claude-mem · error
sync hub push: response requires decimal-string…
Error message
sync hub push: response requires decimal-string head_seq/projected_seq
What it means
The push response's head_seq or projected_seq is not a JSON string (a number, null, or missing). Protocol v2 transmits uint64 sequences as decimal strings because JSON numbers lose precision past 2^53; this is a typeof check only, with assertCanonicalDecimal verifying canonical form immediately after. Nothing is stamped locally; flush() retries via backoff.
Solutions
- Inspect the raw push response (curl with an empty ops array) and confirm both fields are quoted decimal strings
- Fix the hub serializer: String(headSeq), String(projectedSeq) before JSON encoding
- Align hub and client versions so both speak protocol v2
- Add a contract test on the hub asserting typeof body.head_seq === 'string'
Example fix
// hub push handler — before
return Response.json({ acked, head_seq: head, projected_seq: projected });
// after
return Response.json({ acked, head_seq: String(head), projected_seq: String(projected) }); Defensive patterns
Strategy: type-guard
Validate before calling
// Hub-side self-check before responding:
const payload = { acked, head_seq: String(headSeq), projected_seq: String(projectedSeq) };
if (typeof payload.head_seq !== 'string' || typeof payload.projected_seq !== 'string') throw new Error('bad envelope'); Type guard
function isDecimalStringPair(v: { head_seq?: unknown; projected_seq?: unknown }): v is { head_seq: string; projected_seq: string } {
return typeof v.head_seq === 'string' && typeof v.projected_seq === 'string';
} Try / catch
if (/decimal-string head_seq\/projected_seq/.test(sync.status().lastError ?? '')) {
// typeof violation, not formatting: the hub emitted numbers/null — fix the hub serializer
} Prevention
- Serialize uint64 domains with String() on the hub; never let integer JSON defaults reach the wire
- Contract-test both directions (status + push responses) for string-typed sequences
- Beware ports of the hub to other languages — JSON number defaults differ
When it happens
Trigger: Hub serializes the sequences as numbers; one field renamed or dropped in a hub build; an intermediate transform re-encodes them; stub responses in tests using integers.
Common situations: Hub/client version skew after upgrading only one side; a hub port from another language defaulting to integer JSON; contract drift introduced during hub refactoring.
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: acked
- sync hub push: acknowledgment seq exceeds head_seq
- sync hub push: checkpoint order requires head_seq <=…
- sync hub push: distinct operation tuples claimed the same…
- sync hub push: malformed acked
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/427d4bc59b34e011.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:984
}
if (!res.ok) {
const body = (await res.text().catch(() => '')).slice(0, 200);
throw new Error(`sync hub push ${res.status}: ${body}`);
}
let parsed: unknown;
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}]`);
}View on GitHub (pinned to e2d1df569a)