thedotmack/claude-mem · error · Error
sync hub push
Error message
sync hub push ${res.status}: ${body} What it means
pushOps throws this when the sync hub responds with a non-OK HTTP status. The first 200 characters of the response body are included to surface the hub's error detail. It wraps any server-side rejection (auth, protocol, validation, server errors) of a push request.
Solutions
- Read the embedded status and body snippet to identify the hub's rejection reason.
- If 401, refresh the sync token in ~/.claude-mem settings/.env and reconfigure CloudSync.
- If 400/protocol errors, update the client or hub so protocol versions match.
- Retry with backoff for 5xx; check hub availability/proxy config.
Example fix
// before
await sync.pushNow(); // throws opaque
// after
try {
await sync.pushNow();
} catch (e) {
if (/sync hub push 401/.test(e.message)) await refreshTokenAndRetry();
else throw e;
} Defensive patterns
Strategy: retry
Try / catch
try { await client.pushOps(ops); } catch (e) {
const m = /sync hub push (\d{3})/.exec(e.message);
if (m && +m[1] >= 500) await retryWithBackoff(() => client.pushOps(ops));
else if (m && +m[1] === 401) await refreshToken();
else throw e;
} Prevention
- Rotate sync tokens before expiry.
- Keep client and hub protocol versions in lockstep.
- Alert on persistent non-2xx push responses.
When it happens
Trigger: POST to {hubUrl}/v1/sync/ops returns 401 (expired/invalid bearer token), 400 (bad ops or protocol_version mismatch), 413 (body too large), or 5xx while the client expects a PushResponse.
Common situations: Expired sync token after re-auth elsewhere; hub deployed a newer protocol version; hub is temporarily down or behind a misconfigured proxy returning HTML error pages.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- pull
- sync hub pull
- [claude-mem] Worker GET
- [claude-mem] Worker POST
- cloud sync identity unavailable; refusing an unreplicated…
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/be29f97a63d19ced.
Report an issue: GitHub.
Appendix: source
Thrown at src/services/sync/CloudSync.ts:1196
},
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 d8bc9755e7)