{"record":{"id":"ed97367ee228a9e7","repo":"thedotmack/claude-mem","slug":"sync-hub-status-response-status-body","errorCode":null,"errorMessage":"sync hub status ${response.status}: ${body}","messagePattern":"sync hub status (.+?): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/sync/CloudSync.ts","lineNumber":656,"sourceCode":"  private async probeHubStatus(): Promise<void> {\n    let checkedAt = Date.now();\n    try {\n      const response = await this.fetchImpl(`${this.hubUrl}/v1/sync/status`, {\n        method: 'GET',\n        headers: {\n          'Authorization': `Bearer ${this.token}`,\n          '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'","sourceCodeStart":638,"sourceCodeEnd":674,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/sync/CloudSync.ts#L638-L674","documentation":"CloudSync.probeHubStatus fetches the sync hub status endpoint and requires an OK (2xx) HTTP response. Any non-OK response is rejected with 'sync hub status <status>: <first 200 chars of body>', surfacing the hub's own error output. The probe result marks the hub unreachable and the sync mode falls back accordingly.","triggerScenarios":"HTTP response from the hub with status outside 2xx: 401/403 auth failures, 404 wrong URL/path, 500/503 hub-side errors, or gateway 502/504.","commonSituations":"Expired or missing hub credentials, misconfigured hub base URL, hub service down or deploying, reverse proxy returning 502 while the hub restarts.","solutions":["Check the status code and body snippet in the error; fix the corresponding issue (auth token, URL, hub health).","Verify hub authentication credentials/token are present and not expired for 401/403.","Confirm the hub URL/endpoint path is correct for 404s.","Check hub server logs/health for 5xx causes and restart or redeploy the hub if it is unhealthy.","Implement retry with backoff for transient 5xx/gateway responses in statusWithHubProbe."],"exampleFix":"// before\nconst status = await cloudSync.statusWithHubProbe();\n// after\nlet status;\ntry {\n  status = await cloudSync.statusWithHubProbe();\n} catch (e) {\n  if (/sync hub status 5\\d\\d/.test(e.message)) {\n    status = await cloudSync.statusWithHubProbe(); // retry after backoff\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// probe reachability + expected status before the real call\nconst pre = await fetch(hubUrl + '/status', { method: 'HEAD' });\nif (pre.status === 401) throw new Error('hub auth required');\nif (!pre.ok) throw new Error(`hub unhealthy: ${pre.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  const status = await cloudSync.statusWithHubProbe();\n} catch (e) {\n  const m = /^sync hub status (\\d{3}):/.exec(e.message);\n  if (m && +m[1] >= 500) await retryWithBackoff(() => cloudSync.statusWithHubProbe());\n  else if (m && (+m[1] === 401 || +m[1] === 403)) await refreshHubCredentials();\n  else throw e;\n}","preventionTips":["Differentiate 4xx (fix credentials/URL) from 5xx (retry) when handling this error","Log the body snippet included in the message — it names the hub-side cause","Retry transient 502/503/504 with backoff before declaring the hub unreachable","Monitor hub endpoint health independently of sync runs"],"tags":["http","sync-hub","api"],"backgroundTag":"http-error-response","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"}