ruvnet/ruflo · warning · Error

Invalid item ID: ${id}

Error message

Invalid item ID: ${id}

What it means

Thrown by getBulkRatings() during its pre-flight loop when at least one entry of itemIds fails the same validateItemId() regex (/^[@a-zA-Z0-9\/_-]+$/, length < 100). The offending ID is included in the message, making this the diagnostic version of error 422. Validation runs before the batch is truncated to 50, so a bad ID anywhere in the array aborts the whole call.

Source

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

  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}`);
    }
  }

  // Limit batch size
  const limitedIds = itemIds.slice(0, 50);

  const response = await fetch(`${REGISTRY_API_URL}?action=bulk-ratings`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      itemIds: limitedIds,
      itemType,
    }),
    signal: AbortSignal.timeout(15000),
  });

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

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Filter the list before calling — keep only IDs matching /^[@a-zA-Z0-9/_-]+$/ with length < 100 — and log the dropped ones.
  2. The message names the bad ID: parse it out (after 'Invalid item ID: ') to pinpoint the producer of the bad entry and fix it at the source.
  3. Trim whitespace and reject empty strings when building the array.
  4. Remember the API slices to the first 50 IDs: chunk your calls at ≤50 so silent truncation doesn't hide missing ratings.

Example fix

// before
const ratings = await getBulkRatings(allPluginIds); // one bad id aborts everything

// after
const ITEM_ID_RE = /^[@a-zA-Z0-9/_-]+$/;
const validIds = allPluginIds.filter(id => ITEM_ID_RE.test(id) && id.length < 100);
const invalid = allPluginIds.filter(id => !validIds.includes(id));
if (invalid.length) console.warn('Skipping invalid registry ids:', invalid);
const ratings: BulkRatingsResponse = {};
for (let i = 0; i < validIds.length; i += 50) {
  Object.assign(ratings, await getBulkRatings(validIds.slice(i, i + 50)));
}
Defensive patterns

Strategy: validation

Validate before calling

const ITEM_ID_RE = /^[@a-zA-Z0-9/_-]+$/;
function partitionIds(ids: string[]) {
  const valid = ids.filter(id => ITEM_ID_RE.test(id) && id.length < 100);
  const invalid = ids.filter(id => !(ITEM_ID_RE.test(id) && id.length < 100));
  return { valid, invalid };
}
const { valid, invalid } = partitionIds(allIds);
if (invalid.length) console.warn('Dropping invalid registry ids:', invalid);
return getBulkRatings(valid.slice(0, 50)); // API truncates at 50 — chunk yourself

Type guard

function isValidItemId(id: unknown): id is string {
  return typeof id === 'string' && /^[@a-zA-Z0-9/_-]+$/.test(id) && id.length < 100;
}

Try / catch

try {
  return await getBulkRatings(ids);
} catch (e) {
  const m = e instanceof Error ? e.message.match(/^Invalid item ID: (.+)$/) : null;
  if (m) {
    const bad = m[1];
    return getBulkRatings(ids.filter(id => id !== bad)); // drop offender, retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling getBulkRatings(list) where list was aggregated from user input, a config file, or package metadata and one entry contains a dot, space, '@version' suffix, or is ≥100 chars. Zero-length arrays pass validation but produce a pointless request.

Common situations: Mixing npm identifiers (name@version) with registry IDs in one batch; list built by string-splitting that leaves empty strings ('' fails the regex); IDs read from a CSV/JSON with invisible whitespace or BOM; one stale entry from an old schema poisoning every batch.

Related errors


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