ruvnet/ruflo · warning

Rating must be integer 1-5

Error message

Rating must be integer 1-5

What it means

Thrown by rateItem() in the registry API client before any network call: the rating argument failed validateRating(), which requires Number.isInteger(rating) and a value in [1, 5]. This is a client-side input-validation guard protecting the Cloud Functions rating endpoint from garbage input. It fires synchronously, so no HTTP request is wasted.

Source

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

 */
function validateRating(rating: number): boolean {
  return Number.isInteger(rating) && rating >= 1 && rating <= 5;
}

/**
 * Rate a plugin or model
 */
export async function rateItem(
  itemId: string,
  rating: number,
  itemType: 'plugin' | 'model' = 'plugin',
  userId?: string
): Promise<RatingResponse> {
  if (!validateItemId(itemId)) {
    throw new Error('Invalid item ID');
  }
  if (!validateRating(rating)) {
    throw new Error('Rating must be integer 1-5');
  }

  const response = await fetch(`${REGISTRY_API_URL}?action=rate`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      itemId,
      rating,
      itemType,
      ...(userId && { userId }),
    }),
    signal: AbortSignal.timeout(10000),
  });

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Rating failed: ${error}`);
  }

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Convert the input with Number() (or parseInt with radix 10) before calling rateItem, then verify Number.isInteger and the 1–5 range yourself and show a friendly message.
  2. If your UI uses a different scale (e.g. 0–10), map it to 1–5 before calling (Math.max(1, Math.min(5, Math.round(value / 2)))).
  3. Constrain the input at the source: HTML <input type="number" min="1" max="5" step="1"> or an enum of literal 1|2|3|4|5.
  4. Add boundary tests for 0, 1, 5, 6, '3', 3.5 so regressions in parsing surface locally instead of from the library.

Example fix

// before
const rating = parseFloat(args['--rating']); // '4.5' or '0' → throws 'Rating must be integer 1-5'
await rateItem(pluginId, rating);

// after
const rating = Number(args['--rating']);
if (!Number.isInteger(rating) || rating < 1 || rating > 5) {
  throw new RangeError(`Invalid rating '${args['--rating']}': must be an integer 1-5`);
}
await rateItem(pluginId, rating);
Defensive patterns

Strategy: validation

Validate before calling

const isValidRating = (r: unknown): r is number =>
  typeof r === 'number' && Number.isInteger(r) && r >= 1 && r <= 5;

// before calling rateItem:
if (!isValidRating(rawRating)) {
  throw new RangeError(`Rating must be integer 1-5, got ${JSON.stringify(rawRating)}`);
}
await rateItem(itemId, rawRating, 'plugin');

Type guard

function isValidRating(r: unknown): r is number {
  return typeof r === 'number' && Number.isInteger(r) && r >= 1 && r <= 5;
}

Try / catch

try {
  await rateItem(itemId, rating);
} catch (e) {
  if (e instanceof Error && e.message === 'Rating must be integer 1-5') {
    // re-prompt the user / normalize input, then retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Calling rateItem(itemId, rating) with a non-integer number (4.5, NaN, Infinity), an out-of-range integer (0, 6, 10), a numeric string ('4' — fails Number.isInteger), or a value parsed from a star-widget UI that yields 0 on empty selection.

Common situations: Rating comes from a form/CLI flag as a string and is passed without Number() conversion; UI uses a 0–10 or 0–100 scale while the API expects 1–5; parseFloat('4.5') on a half-star widget; passing a default of 0 or -1 when the user skipped the rating.

Related errors


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