{"record":{"id":"3415b687568a63f6","repo":"thedotmack/claude-mem","slug":"projection-fetch-timeout-must-be-strictly-shorter-than-the","errorCode":null,"errorMessage":"projection fetch timeout must be strictly shorter than the Hub lease","messagePattern":"projection fetch timeout must be strictly shorter than the Hub lease","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workers/sync-hub/src/index.ts","lineNumber":95,"sourceCode":" * serialization overhead), and a per-op body that passes DO validation can\n * never hit the SQLite row limit. Oversize requests get a deliberate 413 —\n * never a generic 500 that clients would retry forever.\n */\nconst MAX_OPS_PER_PUSH = 500; // mirrors the getChanges page cap\nconst MAX_PUSH_BODY_BYTES = 8_000_000;\nconst CANONICAL_DECIMAL = /^(?:0|[1-9][0-9]*)$/;\n// Scheduled repair is deliberately incremental. One page is at most 100 ops or\n// 4 MB, so a deeply lagging user cannot monopolize a cron request or keep the\n// per-user projection lease busy while every other user waits.\nconst REPAIR_DRAIN_MAX_PAGES = 1;\n\n/**\n * Pro declares a 60-second maximum duration. Abort the complete response-body\n * read at 45 seconds while the Hub holds a 90-second fencing lease:\n * Hub abort (45s) < Pro platform ceiling (60s) < Hub lease (90s).\n */\nif (PROJECTION_FETCH_TIMEOUT_MS >= PROJECTION_LEASE_MS) {\n\tthrow new Error(\"projection fetch timeout must be strictly shorter than the Hub lease\");\n}\n\nconst encoder = new TextEncoder();\n\nfunction json(status: number, data: unknown): Response {\n\treturn new Response(JSON.stringify(data), {\n\t\tstatus,\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t});\n}\n\nfunction errorResponse(status: number, error: string): Response {\n\treturn json(status, { error });\n}\n\ninterface AuthOk {\n\tok: true;\n\tuserId: string;","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/workers/sync-hub/src/index.ts#L77-L113","documentation":"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.","triggerScenarios":"Thrown at workers/sync-hub/src/index.ts:95 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Lower PROJECTION_FETCH_TIMEOUT_MS so it stays strictly below PROJECTION_LEASE_MS, preserving the documented 45s < 60s < 90s ordering","Raise PROJECTION_LEASE_MS if the fetch genuinely needs more time, keeping a safe margin below the Worker platform ceiling","Add a test or CI check asserting the ordering of the three timing constants before deployment"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}