thedotmack/claude-mem · error · Error

projection fetch timeout must be strictly shorter than the…

Error message

projection fetch timeout must be strictly shorter than the Hub lease

What it means

This is a startup configuration invariant in the sync-hub Worker module scope. The projection fetch timeout (PROJECTION_FETCH_TIMEOUT_MS, 45s) is asserted to be strictly less than the Hub fencing lease (PROJECTION_LEASE_MS, 90s), so an aborted upstream fetch always completes before the lease the Hub holds expires. If the constants are edited so the timeout meets or exceeds the lease, a timed-out fetch could overlap a new lease holder, breaking fencing; the module deliberately refuses to load by throwing at import time rather than allowing that race in production.

Solutions

  1. Lower PROJECTION_FETCH_TIMEOUT_MS so it stays strictly below PROJECTION_LEASE_MS, preserving the documented 45s < 60s < 90s ordering
  2. Raise PROJECTION_LEASE_MS if the fetch genuinely needs more time, keeping a safe margin below the Worker platform ceiling
  3. Add a test or CI check asserting the ordering of the three timing constants before deployment
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at workers/sync-hub/src/index.ts:95 when the library encounters an invalid state.

Common situations: See trigger scenarios.

Understand the failure class


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/3415b687568a63f6. Report an issue: GitHub.

Appendix: source

Thrown at workers/sync-hub/src/index.ts:95

 * serialization overhead), and a per-op body that passes DO validation can
 * never hit the SQLite row limit. Oversize requests get a deliberate 413 —
 * never a generic 500 that clients would retry forever.
 */
const MAX_OPS_PER_PUSH = 500; // mirrors the getChanges page cap
const MAX_PUSH_BODY_BYTES = 8_000_000;
const CANONICAL_DECIMAL = /^(?:0|[1-9][0-9]*)$/;
// Scheduled repair is deliberately incremental. One page is at most 100 ops or
// 4 MB, so a deeply lagging user cannot monopolize a cron request or keep the
// per-user projection lease busy while every other user waits.
const REPAIR_DRAIN_MAX_PAGES = 1;

/**
 * Pro declares a 60-second maximum duration. Abort the complete response-body
 * read at 45 seconds while the Hub holds a 90-second fencing lease:
 * Hub abort (45s) < Pro platform ceiling (60s) < Hub lease (90s).
 */
if (PROJECTION_FETCH_TIMEOUT_MS >= PROJECTION_LEASE_MS) {
	throw new Error("projection fetch timeout must be strictly shorter than the Hub lease");
}

const encoder = new TextEncoder();

function json(status: number, data: unknown): Response {
	return new Response(JSON.stringify(data), {
		status,
		headers: { "Content-Type": "application/json" },
	});
}

function errorResponse(status: number, error: string): Response {
	return json(status, { error });
}

interface AuthOk {
	ok: true;
	userId: string;

View on GitHub (pinned to d8bc9755e7)