ruvnet/ruflo · error
Failed to get ratings
Error message
Failed to get ratings
What it means
Thrown by getRating() when the GET to publish-registry?action=get-ratings&itemId=... returns a non-2xx status. Unlike rateItem(), no response body is included in the message, so this is a bare 'the ratings endpoint said no'. The fetch carries a 10s AbortSignal timeout, so reaching this line means the server responded with an error status.
Source
Thrown at v3/@claude-flow/cli/src/services/registry-api.ts:109
itemId: string,
itemType: 'plugin' | 'model' = 'plugin'
): Promise<RatingResponse> {
if (!validateItemId(itemId)) {
throw new Error('Invalid item ID');
}
const params = new URLSearchParams({
action: 'get-ratings',
itemId,
itemType,
});
const response = await fetch(`${REGISTRY_API_URL}?${params}`, {
signal: AbortSignal.timeout(10000),
});
if (!response.ok) {
throw new Error('Failed to get ratings');
}
return response.json() as Promise<RatingResponse>;
}
/**
* Get ratings for multiple items (batch)
*/
export async function getBulkRatings(
itemIds: string[],
itemType: 'plugin' | 'model' = 'plugin'
): Promise<BulkRatingsResponse> {
// Validate all IDs
for (const id of itemIds) {
if (!validateItemId(id)) {
throw new Error(`Invalid item ID: ${id}`);
}
}View on GitHub (pinned to fa13ee4ad6)
Solutions
- Retry with backoff on 5xx/429 — the call is a read, so retrying is safe; treat persistent 404 as 'no ratings yet'.
- Wrap the call and fall back to { average: 0, count: 0 } so UI rendering never depends on registry availability.
- Cache ratings locally with a TTL instead of fetching on every render; this also keeps you inside rate limits.
- Confirm the itemId is registered (it must have been published) — an unpublishable ID yields server errors, not the client-side 'Invalid item ID'.
Example fix
// before
const { average } = await getRating(pluginId); // may throw 'Failed to get ratings'
// after
async function safeRating(pluginId: string) {
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await getRating(pluginId);
} catch (e) {
if (attempt === 2) return { success: false, itemId: pluginId, average: 0, count: 0 };
await new Promise(r => setTimeout(r, 250 * 2 ** attempt));
}
}
throw new Error('unreachable');
} Defensive patterns
Strategy: retry
Try / catch
const EMPTY = (id: string) => ({ success: false, itemId: id, average: 0, count: 0 });
async function getRatingSafe(id: string, tries = 3) {
for (let i = 0; ; i++) {
try { return await getRating(id); }
catch (e) {
if (i === tries - 1) return EMPTY(id); // degrade, don't crash
if (e instanceof Error && e.message === 'Invalid item ID') return EMPTY(id); // don't retry client errors
await new Promise(r => setTimeout(r, 300 * 2 ** i));
}
}
} Prevention
- Treat ratings as optional enrichment: always render UI with a 0/0 default.
- Cache getRating results with a TTL to avoid hammering the Cloud Function into 429s.
- Retry only transient failures with backoff; never retry the 'Invalid item ID' sibling error.
- In CI, stub the registry client rather than depending on live network availability.
When it happens
Trigger: Cloud Function returns 404/500 for an unknown or never-rated itemId (some backends 404 instead of returning count: 0), 429 from rate limiting, or 5xx during cold start; an intermediary proxy returns 4xx/5xx.
Common situations: Fetching ratings for a freshly published plugin before any ratings exist; polling the endpoint in a loop and tripping rate limits; CI with proxied egress; regional outage of the us-central1 Cloud Function.
Related errors
- Rating failed: ${error}
- Failed to get bulk ratings
- Failed to get analytics
- Failed to fetch ${baseURL}/models: ${response.status} ${resp
- Failed to fetch manifest from ${url}: ${res.status} ${res.st
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/3d93abe5f23c1064.
Report an issue: GitHub.