{"record":{"id":"8893e6bfa48857e9","repo":"ruvnet/ruflo","slug":"failed-to-get-bulk-ratings","errorCode":null,"errorMessage":"Failed to get bulk ratings","messagePattern":"Failed to get bulk ratings","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/registry-api.ts","lineNumber":143,"sourceCode":"      throw new Error(`Invalid item ID: ${id}`);\n    }\n  }\n\n  // Limit batch size\n  const limitedIds = itemIds.slice(0, 50);\n\n  const response = await fetch(`${REGISTRY_API_URL}?action=bulk-ratings`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({\n      itemIds: limitedIds,\n      itemType,\n    }),\n    signal: AbortSignal.timeout(15000),\n  });\n\n  if (!response.ok) {\n    throw new Error('Failed to get bulk ratings');\n  }\n\n  return response.json() as Promise<BulkRatingsResponse>;\n}\n\n/**\n * Get analytics data\n */\nexport async function getAnalytics(): Promise<AnalyticsResponse> {\n  const response = await fetch(`${REGISTRY_API_URL}?action=analytics`, {\n    signal: AbortSignal.timeout(10000),\n  });\n\n  if (!response.ok) {\n    throw new Error('Failed to get analytics');\n  }\n\n  return response.json() as Promise<AnalyticsResponse>;","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/services/registry-api.ts#L125-L161","documentation":"Thrown by getBulkRatings() when the POST to publish-registry?action=bulk-ratings returns non-2xx. This call has the longest budget in the file (AbortSignal.timeout(15000)) because it ships up to 50 IDs, so hitting this error means the server answered within 15s but with an error status. No response body is surfaced.","triggerScenarios":"Server rejects the batch payload (unknown itemType, oversized list server-side), returns 429 under rate limiting, or 5xx during cold start; a proxy intercepts the POST; intermittent failures appear only with large batches near the 50-item cap.","commonSituations":"Dashboard fetching ratings for dozens of plugins every page load and tripping quotas; a registry deploy temporarily breaking the bulk action; environments where only small POSTs pass a WAF/body-size filter.","solutions":["Retry with backoff (safe read-only operation), then fall back to per-item getRating() calls or an empty result.","Reduce batch size (e.g. 25 per call) to shrink the payload and narrow server-side failures.","Cache bulk results with a TTL and refresh in the background instead of per-request.","Check the itemId list for correctness — server-side rejection of itemType or unknown IDs only surfaces as this generic error."],"exampleFix":"// before\nconst all = await getBulkRatings(ids); // single point of failure\n\n// after\nasync function bulkRatingsSafe(ids: string[]): Promise<BulkRatingsResponse> {\n  try {\n    return await getBulkRatings(ids.slice(0, 50));\n  } catch {\n    const out: BulkRatingsResponse = {};\n    for (const id of ids) out[id] = { average: 0, count: 0 };\n    return out;\n  }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"async function bulkRatingsResilient(ids: string[]): Promise<BulkRatingsResponse> {\n  const out: BulkRatingsResponse = {};\n  for (let i = 0; i < ids.length; i += 50) {\n    const chunk = ids.slice(i, i + 50);\n    for (let attempt = 0; ; attempt++) {\n      try { Object.assign(out, await getBulkRatings(chunk)); break; }\n      catch (e) {\n        if (e instanceof Error && e.message.startsWith('Invalid item ID')) throw e; // fix input, don't retry\n        if (attempt >= 2) { chunk.forEach(id => (out[id] = { average: 0, count: 0 })); break; }\n        await new Promise(r => setTimeout(r, 400 * 2 ** attempt));\n      }\n    }\n  }\n  return out;\n}","preventionTips":["Chunk to ≤50 IDs per call — smaller payloads fail less and isolate faults.","Cache bulk results with a TTL; refresh in the background rather than per request.","Never retry 'Invalid item ID' errors (error 424) — retrying validation failures just burns quota.","Ship a zero-value fallback shape so dashboards survive registry outages."],"tags":["network","http","batch","ratings","registry-api"],"backgroundTag":"http-request-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}