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
- Filter the list before calling — keep only IDs matching /^[@a-zA-Z0-9/_-]+$/ with length < 100 — and log the dropped ones.
- 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.
- Trim whitespace and reject empty strings when building the array.
- 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
- Sanitize the whole array at build time — one bad ID aborts the entire batch otherwise.
- Split batches into chunks of ≤50; the API silently drops anything past the 50th ID.
- Trim whitespace and drop empty strings when assembling ID lists from config/user input.
- Log dropped IDs with their source so the producer of bad IDs gets fixed, not just filtered.
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
- Rating must be integer 1-5
- Failed to get bulk ratings
- Invalid worker type
- localCompute: no adapter for graphId=${input.graphId}
- signAttributionArtifact: privateKey must be 32 bytes (got ${
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/ab591f41b5c2f51f.
Report an issue: GitHub.