thedotmack/claude-mem · error · Error
push
Error message
push ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)} What it means
The sync-hub canary's push method POSTs an operation to the hub and expects an OK response containing an ack for the operation's SHA-256. If the HTTP response status is not ok, it throws with the status code and the first 200 characters of the response body (body read failures degrade to an empty string). This surfaces server-side rejection details for debugging the sync protocol.
Solutions
- Inspect the response body snippet in the error to identify the hub's rejection reason
- Handle 409 conflicts idempotently — treat already-acked operations as success
- Retry with backoff on 5xx/503 responses
- Check hub auth/credentials if the status is 401/403
Example fix
// before
await canary.push(body, operationSha256); // throws on !res.ok
// after
try {
await canary.push(body, operationSha256);
} catch (e) {
if (String(e).startsWith('push 409')) return; // already acked
await backoffRetry(() => canary.push(body, operationSha256));
} Defensive patterns
Strategy: retry
Validate before calling
const body = JSON.stringify(ops);
if (body.length > MAX_PUSH_BYTES) throw new Error('operation too large for single push'); Try / catch
try { await client.push(body, sha); }
catch (e) {
if (/push 409/.test(String(e))) return; // duplicate op, treat as acked
if (/push 5\d\d/.test(String(e))) { await backoffRetry(() => client.push(body, sha)); return; }
throw e;
} Prevention
- Make pushes idempotent via operation_sha256 and treat 409 as success
- Use retry with exponential backoff for 5xx responses
- Keep operation bodies under hub size limits
- Refresh auth tokens before they expire on long-running canaries
When it happens
Trigger: The hub returns a non-2xx status to the push request — e.g. 409 conflict on a duplicate operation_sha256, 413 payload too large, 500 server error, 503 during maintenance.
Common situations: Replaying an operation the hub already acked; hub deployment/maintenance window; oversized operation bodies; auth token expiry on the canary client.
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
- BadRequest
- BadRequest
- cloud sync identity unavailable; refusing an unreplicated…
- cloud sync unavailable; refusing an unreplicated delete
- Forbidden
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/01d49568147a7b72.
Report an issue: GitHub.
Appendix: source
Thrown at workers/sync-hub/canary/canary.ts:240
origin_local_id: localId,
payload,
payload_schema_version: 2,
payload_sha256: await sha256Base64Url(canonicalJson(payload)),
};
const body = canonicalJson(envelope);
const operationSha256 = await sha256Base64Url(body);
const res = await fetch(`${this.args.hub}/v1/sync/ops`, {
method: "POST",
headers: { ...this.headers(deviceId), "Content-Type": "application/json" },
body: JSON.stringify({
protocol_version: 2,
ops: [{ body, operation_sha256: operationSha256 }],
}),
signal: AbortSignal.timeout(this.args.timeoutMs),
});
this.noteMode(res);
if (!res.ok) {
throw new Error(`push ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}`);
}
const parsed = (await res.json()) as {
acked: Array<{ operation_sha256: string; seq: string }>;
head_seq: string;
};
const ack = parsed.acked.find((a) => a.operation_sha256 === operationSha256);
if (!ack) throw new Error("push response did not ack the canary op");
return { seq: ack.seq, headSeq: parsed.head_seq, opSha: operationSha256 };
}
private async pull(
deviceId: string,
since: string,
): Promise<{ ops: Array<{ seq: string; operation_sha256: string }>; headSeq: string }> {
const res = await fetch(`${this.args.hub}/v1/sync/changes?since=${since}&limit=500`, {
headers: this.headers(deviceId),
signal: AbortSignal.timeout(this.args.timeoutMs),
});View on GitHub (pinned to d8bc9755e7)