thedotmack/claude-mem · error · Error
sync hub status: response requires decimal-string…
Error message
sync hub status: response requires decimal-string epoch/head_seq/projected_seq
What it means
The protocol requires epoch, head_seq and projected_seq to be strings containing canonical decimal integers (arbitrary precision). probeHubStatus throws 'sync hub status: response requires decimal-string epoch/head_seq/projected_seq' when any field is missing, not a string, or not a canonical decimal, before further arithmetic via assertCanonicalDecimal/compareCanonicalDecimals.
Solutions
- Fix the hub to serialize epoch/head_seq/projected_seq as decimal strings, e.g. String(seq).
- Reject/handle numeric sequences at the client edge by normalizing before the probe if you control both sides.
- Validate payloads in hub tests with assertCanonicalDecimal to catch non-canonical forms early.
- Confirm field names are exactly epoch, head_seq, projected_seq (lowercase, snake_case).
Example fix
// before (hub)
res.json({ protocol_version: 2, epoch: 1726000000, head_seq: 42, projected_seq: 42 });
// after
res.json({ protocol_version: 2, epoch: '1726000000', head_seq: '42', projected_seq: '42' }); Defensive patterns
Strategy: validation
Validate before calling
function isCanonicalDecimal(v: unknown): v is string {
return typeof v === 'string' && /^\d+$/.test(v) && (v === '0' || !v.startsWith('0'));
}
if (!isCanonicalDecimal(body.epoch) || !isCanonicalDecimal(body.head_seq) || !isCanonicalDecimal(body.projected_seq)) {
throw new Error('hub seq fields must be canonical decimal strings');
} Type guard
function isDecimalStringSeq(v: unknown): v is string {
return typeof v === 'string' && /^\d+$/.test(v);
} Try / catch
try {
const status = await cloudSync.statusWithHubProbe();
} catch (e) {
if (e.message.includes('decimal-string epoch/head_seq/projected_seq')) {
logger.error('hub serializes seq counters in wrong format (numbers/non-canonical strings)', { raw: lastRawBody });
} else throw e;
} Prevention
- Always serialize sequence counters as JSON strings, never numbers
- Reject numeric or non-canonical seq values in hub tests with assertCanonicalDecimal
- Keep field names exactly epoch/head_seq/projected_seq in hub implementations
- Add a schema test asserting the full status payload shape
When it happens
Trigger: Hub returns these fields as numbers instead of strings, omits one of them, uses non-canonical forms (leading '+', leading zeros, whitespace, empty string), or sends null.
Common situations: A hub implementation serializing seq counters as JSON numbers, a hand-rolled hub using different field names or formats, JSON fixtures written by hand with numeric epochs.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- sync hub status: response must be an object
- Invalid transcript watch config
- schema v46: . row is not a positive canonical uint64…
- ServerGenerationJobPayloadValidationError
- SyncApply: invalid op seq=
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/b15aafcd889caed6.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:677
let parsed: unknown;
try {
parsed = await response.json();
} catch {
throw new Error('sync hub status: response is not JSON');
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('sync hub status: response must be an object');
}
const record = parsed as Record<string, unknown>;
if (record.protocol_version !== 2) {
throw new Error('sync hub status: response requires protocol_version 2');
}
if (
typeof record.epoch !== 'string'
|| typeof record.head_seq !== 'string'
|| typeof record.projected_seq !== 'string'
) {
throw new Error('sync hub status: response requires decimal-string epoch/head_seq/projected_seq');
}
const epoch = assertCanonicalDecimal(record.epoch, { positive: true });
const headSeq = assertCanonicalDecimal(record.head_seq);
const projectedSeq = assertCanonicalDecimal(record.projected_seq);
if (compareCanonicalDecimals(projectedSeq, headSeq) > 0) {
throw new Error('sync hub status: projected_seq exceeds head_seq');
}
this.hubStatus = {
checkedAt,
reachable: true,
epoch,
headSeq,
projectedSeq,
error: null,
};
} catch (error) {
const raw = error instanceof Error ? error.message : String(error);
const safe = this.token === '' ? raw : raw.split(this.token).join('[REDACTED]');View on GitHub (pinned to d8bc9755e7)