{"record":{"id":"70acff8c9a3019aa","repo":"thedotmack/claude-mem","slug":"sync-hub-status-response-is-not-json","errorCode":null,"errorMessage":"sync hub status: response is not JSON","messagePattern":"sync hub status: response is not JSON","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/sync/CloudSync.ts","lineNumber":603,"sourceCode":"          'X-User-Id': this.userId,\n          'X-Device-Id': this.deviceId,\n          ...(this.deviceName ? { 'X-Device-Name': this.deviceName } : {}),\n        },\n        signal: AbortSignal.timeout(this.requestTimeoutMs),\n      });\n      checkedAt = Date.now();\n      const syncMode = response.headers.get('X-Sync-Mode');\n      if (syncMode !== null || response.ok) this.emitSyncMode(syncMode);\n      if (!response.ok) {\n        const body = (await response.text().catch(() => '')).slice(0, 200);\n        throw new Error(`sync hub status ${response.status}: ${body}`);\n      }\n\n      let parsed: unknown;\n      try {\n        parsed = await response.json();\n      } catch {\n        throw new Error('sync hub status: response is not JSON');\n      }\n      if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n        throw new Error('sync hub status: response must be an object');\n      }\n      const record = parsed as Record<string, unknown>;\n      if (record.protocol_version !== 2) {\n        throw new Error('sync hub status: response requires protocol_version 2');\n      }\n      if (\n        typeof record.epoch !== 'string'\n        || typeof record.head_seq !== 'string'\n        || typeof record.projected_seq !== 'string'\n      ) {\n        throw new Error('sync hub status: response requires decimal-string epoch/head_seq/projected_seq');\n      }\n      const epoch = assertCanonicalDecimal(record.epoch, { positive: true });\n      const headSeq = assertCanonicalDecimal(record.head_seq);\n      const projectedSeq = assertCanonicalDecimal(record.projected_seq);","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/services/sync/CloudSync.ts#L585-L621","documentation":"Thrown by probeHubStatus when response.ok but response.json() threw — the hub returned 200 with a body that is not valid JSON (or the stream errored). This is a protocol/contract violation: a 200 on /v1/sync/status must be a JSON object.","triggerScenarios":"The hub returned 200 but the body was HTML (error page from a misconfigured proxy/CDN), empty, truncated, or text; a transparent proxy returned a captive-portal page; the response Content-Type was not application/json and the body was not parseable.","commonSituations":"A reverse proxy/CDN intercepting the request and returning an HTML error page with status 200; hub misconfigured to serve a status page instead of JSON; network middlebox truncating the body; pointing hubUrl at a non-hub host.","solutions":["Capture the raw response body and Content-Type header to see what the hub actually returned.","Verify hubUrl points at the real sync hub and not a generic web server / captive portal.","Check for an intercepting proxy/gateway returning HTML; bypass it or fix its routing.","If the hub itself is serving non-JSON on 200, that is a hub bug — report it to the hub operator.","Ensure no middleware rewrites 5xx into 200-with-HTML."],"exampleFix":"// before: hubUrl hits a generic web server\nhubUrl = 'https://example.com';\n// after: hubUrl hits the actual sync hub API base\nhubUrl = 'https://sync.example.com'; // serves /v1/sync/status as JSON","handlingStrategy":"validation","validationCode":null,"typeGuard":"function isHubStatusNotJson(e: unknown): boolean {\n  return e instanceof Error && /sync hub status: response is not JSON/i.test(e.message);\n}","tryCatchPattern":"try { await cloudSync.status(); }\ncatch (e) { if (isHubStatusNotJson(e)) { markHubMisconfigured(e.message); return; } throw e; }","preventionTips":["Confirm hubUrl serves the JSON sync API, not an HTML site or captive portal.","Bypass or fix reverse proxies/CDNs that return HTML on 200.","Validate the response Content-Type is application/json in integration tests against the hub.","Capture the raw body once to diagnose which middlebox is rewriting responses."],"tags":["cloud-sync","http","json","hub","protocol"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}