thedotmack/claude-mem · error
sync hub push ${res.status}: ${body}
Error message
sync hub push ${res.status}: ${body} What it means
Thrown by the push response handler when the POST to ${hubUrl}/v1/sync/ops returned a non-OK status. The body is read (up to 200 chars) and included. X-Sync-Mode header is emitted on presence or OK, but absence on an error response is deliberately NOT emitted as a clear (per the documented asymmetric contract) so a degraded-auth 503 does not read as 'switch cleared'.
Source
Thrown at src/services/sync/CloudSync.ts:969
},
body: requestBody,
signal: AbortSignal.timeout(this.requestTimeoutMs),
});
// Mode hint BEFORE the ok-check: the kill-switch header rides error
// responses too, and a client that only learned the mode from happy
// paths would keep hammering the socket through an incident.
// Asymmetric on purpose (SyncClient.onSyncModeHint contract): header
// PRESENCE is emitted regardless of status; header ABSENCE is only
// emitted (as null = "cleared") from an OK response — absence on an
// error response is ambiguous (a degraded auth upstream 503s without
// the funnel) and must not read as "switch cleared".
const syncMode = res.headers.get('X-Sync-Mode');
if (syncMode !== null || res.ok) {
this.emitSyncMode(syncMode);
}
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);View on GitHub (pinned to d768ba3643)
Solutions
- Read the status code and body in the message — 401/403 → re-auth; 409 → re-sync from status; 429 → back off; 5xx → retry with backoff.
- Re-run probeHubStatus to refresh epoch/head_seq before retrying the push, in case of a 409 conflict.
- Verify the token is still valid and X-User-Id/X-Device-Id are correct.
- For 429, reduce push frequency / batch more aggressively.
- For 5xx, retry with exponential backoff and surface a hub-outage indication to the user.
Example fix
// before: push on a stale epoch hits 409 and aborts
await cloudSync.pushOps(ops);
// after: refresh status then retry on conflict
try { await cloudSync.pushOps(ops); }
catch (e) { if (/push 409/.test(e.message)) { await cloudSync.status(); await backoff(); await cloudSync.pushOps(ops); } else throw e; } Defensive patterns
Strategy: retry
Type guard
function isPushHttpError(e: unknown): boolean {
return e instanceof Error && /^sync hub push \d{3}:/i.test(e.message);
} Try / catch
try { await cloudSync.pushOps(ops); }
catch (e) {
if (isPushHttpError(e)) {
const code = Number(e.message.match(/push (\d{3})/)?.[1]);
if (code === 409) { await cloudSync.status(); await sleep(500); await cloudSync.pushOps(ops); }
else if (code === 429 || code >= 500) { await sleep(backoffMs); await cloudSync.pushOps(ops); }
else { throw e; }
return;
}
throw e;
} Prevention
- Refresh the token before it expires to avoid 401/403 on push.
- Re-probe status to refresh epoch/head_seq before retrying on 409 conflicts.
- Back off pushes on 429; batch more to reduce request count.
- Rely on hub dedup (origin_device, kind, origin_id, rev) — re-pushing un-acked ops is safe.
When it happens
Trigger: The hub rejected the push: 401/403 (token/user/device invalid), 409 (sequence conflict / epoch mismatch), 413 (body too large, though 115 guards client-side), 422 (malformed ops), 429 (rate limit), 5xx (hub outage).
Common situations: Token expired between status and push; out-of-sync epoch/head_seq causing a 409; hub rate-limiting a chatty client; partial hub outage; client pushing ops the hub considers malformed after a protocol change.
Related errors
- sync hub status ${response.status}: ${body}
- sync hub status: response is not JSON
- sync hub push: response is not JSON
- sync hub push: response missing acked array
- sync hub push: response requires decimal-string head_seq/pro
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/be29f97a63d19ced.
Report an issue: GitHub.