thedotmack/claude-mem · error · Error
sync hub status: response must be an object
Error message
sync hub status: response must be an object
What it means
After parsing, probeHubStatus validates that the hub status payload is a JSON object. Arrays, null, or primitives are rejected with 'sync hub status: response must be an object' because the sync-hub protocol record is expected to be a flat object of protocol fields.
Solutions
- Check the hub version; upgrade or downgrade so it serves the sync-hub status protocol object.
- Inspect the raw response body to see what shape the hub actually returned.
- Confirm you are hitting the status endpoint, not a list/collections endpoint.
- Fix custom/mock hub implementations to return a JSON object at the top level.
Example fix
// before
const parsed = JSON.parse(body);
// after (hub-side or client pre-check)
const parsed = JSON.parse(body);
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('hub status payload must be a JSON object, got: ' + JSON.stringify(parsed).slice(0, 100));
} Defensive patterns
Strategy: validation
Validate before calling
const res = await fetch(hubUrl + '/status');
const parsed = await res.json();
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('hub returned non-object status payload');
} Type guard
function isStatusRecord(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Try / catch
try {
const status = await cloudSync.statusWithHubProbe();
} catch (e) {
if (e.message === 'sync hub status: response must be an object') {
logger.error('hub protocol shape mismatch — check hub version', { bodySniff: lastRawBody });
} else throw e;
} Prevention
- Pin client and hub to compatible sync-hub protocol versions
- Contract-test hub responses against the expected status object shape
- Log the raw payload when validation fails to identify the real shape
- Verify you are hitting the status endpoint, not a list endpoint
When it happens
Trigger: Hub returns a JSON array, null, a string/number, or a top-level structure that is not a plain object.
Common situations: Hub version mismatch returning an older/different payload shape, a load balancer or mock returning a list, a custom hub implementation deviating from the protocol.
Related errors
- sync hub status: response requires decimal-string…
- 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/e2ebce574b9441e5.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:666
},
signal: AbortSignal.timeout(this.requestTimeoutMs),
});
checkedAt = Date.now();
const syncMode = response.headers.get('X-Sync-Mode');
if (syncMode !== null || response.ok) this.emitSyncMode(syncMode);
if (!response.ok) {
const body = (await response.text().catch(() => '')).slice(0, 200);
throw new Error(`sync hub status ${response.status}: ${body}`);
}
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');
}View on GitHub (pinned to d8bc9755e7)