ruvnet/ruflo · error · Error

Failed to get analytics

Error message

Failed to get analytics

What it means

Thrown by getAnalytics() when the GET to publish-registry?action=analytics returns non-2xx. It is the simplest endpoint in the client (no parameters, 10s timeout), so a failure here usually indicates the registry service itself or the network path, not your inputs. No response body is included.

Source

Thrown at v3/@claude-flow/cli/src/services/registry-api.ts:158

  });

  if (!response.ok) {
    throw new Error('Failed to get bulk ratings');
  }

  return response.json() as Promise<BulkRatingsResponse>;
}

/**
 * Get analytics data
 */
export async function getAnalytics(): Promise<AnalyticsResponse> {
  const response = await fetch(`${REGISTRY_API_URL}?action=analytics`, {
    signal: AbortSignal.timeout(10000),
  });

  if (!response.ok) {
    throw new Error('Failed to get analytics');
  }

  return response.json() as Promise<AnalyticsResponse>;
}

/**
 * Track a download event
 */
export async function trackDownload(pluginId: string): Promise<void> {
  if (!validateItemId(pluginId)) {
    return; // Silently fail for invalid IDs
  }

  try {
    await fetch(`${REGISTRY_API_URL}?action=track-download`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ pluginId }),

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Retry with exponential backoff on transient statuses, then degrade gracefully — analytics is telemetry, not a dependency.
  2. Fall back to zeros ({ downloads: {}, exports: 0, imports: 0, publishes: 0 }) or cached last-known values.
  3. Lengthen your polling interval or cache responses to stay under rate limits.
  4. Verify with curl whether the endpoint is down for everyone (action=analytics needs no auth/params) before debugging code.

Example fix

// before
const analytics = await getAnalytics(); // throws, breaks the dashboard

// after
const EMPTY: AnalyticsResponse = { downloads: {}, exports: 0, imports: 0, publishes: 0 };
let cached: AnalyticsResponse = EMPTY;
async function analyticsSafe(): Promise<AnalyticsResponse> {
  try { return (cached = await getAnalytics()); }
  catch { return cached; }
}
Defensive patterns

Strategy: retry

Try / catch

const EMPTY: AnalyticsResponse = { downloads: {}, exports: 0, imports: 0, publishes: 0 };
async function analyticsResilient(): Promise<AnalyticsResponse> {
  for (let attempt = 0; ; attempt++) {
    try { return await getAnalytics(); }
    catch (e) {
      if (attempt >= 2) return EMPTY;
      await new Promise(r => setTimeout(r, 500 * 2 ** attempt));
    }
  }
}

Prevention

When it happens

Trigger: Cloud Function outage/cold-start 5xx, rate limiting (429), or an intermediary (proxy, captive portal, firewall) answering with an error status for the analytics action.

Common situations: Status dashboards polling analytics on a timer and hitting quotas; CI smoke tests that treat analytics availability as a gate; running behind corporate proxies during registry maintenance windows.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/84fc09e3c26cef41. Report an issue: GitHub.