{"record":{"id":"e2ebce574b9441e5","repo":"thedotmack/claude-mem","slug":"sync-hub-status-response-must-be-an-object","errorCode":null,"errorMessage":"sync hub status: response must be an object","messagePattern":"sync hub status: response must be an object","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/sync/CloudSync.ts","lineNumber":666,"sourceCode":"        },\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);\n      if (compareCanonicalDecimals(projectedSeq, headSeq) > 0) {\n        throw new Error('sync hub status: projected_seq exceeds head_seq');\n      }","sourceCodeStart":648,"sourceCodeEnd":684,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/CloudSync.ts#L648-L684","documentation":"After parsing, probeHubStatus validates that the hub status payload is a JSON object. Arrays, null, or primitives are rejected with 'sync hub status: response must be an object' because the sync-hub protocol record is expected to be a flat object of protocol fields.","triggerScenarios":"Hub returns a JSON array, null, a string/number, or a top-level structure that is not a plain object.","commonSituations":"Hub version mismatch returning an older/different payload shape, a load balancer or mock returning a list, a custom hub implementation deviating from the protocol.","solutions":["Check the hub version; upgrade or downgrade so it serves the sync-hub status protocol object.","Inspect the raw response body to see what shape the hub actually returned.","Confirm you are hitting the status endpoint, not a list/collections endpoint.","Fix custom/mock hub implementations to return a JSON object at the top level."],"exampleFix":"// before\nconst parsed = JSON.parse(body);\n// after (hub-side or client pre-check)\nconst parsed = JSON.parse(body);\nif (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n  throw new Error('hub status payload must be a JSON object, got: ' + JSON.stringify(parsed).slice(0, 100));\n}","handlingStrategy":"validation","validationCode":"const res = await fetch(hubUrl + '/status');\nconst parsed = await res.json();\nif (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n  throw new Error('hub returned non-object status payload');\n}","typeGuard":"function isStatusRecord(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const status = await cloudSync.statusWithHubProbe();\n} catch (e) {\n  if (e.message === 'sync hub status: response must be an object') {\n    logger.error('hub protocol shape mismatch — check hub version', { bodySniff: lastRawBody });\n  } else throw e;\n}","preventionTips":["Pin client and hub to compatible sync-hub protocol versions","Contract-test hub responses against the expected status object shape","Log the raw payload when validation fails to identify the real shape","Verify you are hitting the status endpoint, not a list endpoint"],"tags":["schema","sync-hub","validation"],"backgroundTag":"unexpected-response-shape","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"}