koala73/worldmonitor · error · Error

Custom scorecard bloc members must belong to the public rank

Error message

Custom scorecard bloc members must belong to the public rankable country universe.

What it means

Even well-formed, unique, uppercase ISO-2 codes are only accepted if each belongs to the public rankable country universe (checked via isInRankableUniverse(member)). The scorecard ranks countries against a curated dataset universe; microstates, territories, or codes without underlying indicator data are excluded so blocs stay comparable. Codes outside the universe (e.g., 'VA', 'HK' depending on the current universe, or 'AQ') throw this error.

Source

Thrown at server/worldmonitor/scorecard/v1/_bloc-presets.ts:42

};

export function resolveBlocSelection(input: { preset?: string; members?: string[] }): ScorecardBlocSelection {
  const preset = String(input.preset || '').trim();
  const members = Array.isArray(input.members) ? input.members : [];
  if (Boolean(preset) === (members.length > 0)) {
    throw new Error('Select exactly one scorecard bloc preset or custom member list.');
  }
  if (preset) {
    if (!(preset in SCORECARD_BLOC_PRESETS)) throw new Error(`Unknown scorecard bloc preset: ${preset}`);
    return SCORECARD_BLOC_PRESETS[preset as ScorecardBlocPreset];
  }
  if (members.length < 2 || members.length > 30) throw new Error('Custom scorecard blocs require 2-30 members.');
  if (members.some((member) => !/^[A-Z]{2}$/.test(member))) {
    throw new Error('Custom scorecard bloc members must be uppercase ISO-2 codes.');
  }
  if (new Set(members).size !== members.length) throw new Error('Custom scorecard bloc members must be unique.');
  if (members.some((member) => !isInRankableUniverse(member))) {
    throw new Error('Custom scorecard bloc members must belong to the public rankable country universe.');
  }
  const sorted = [...members].sort();
  return { id: `custom:${sorted.join('-')}`, label: sorted.join(' + '), members: sorted };
}

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Filter members through the published rankable-universe list (or isInRankableUniverse if you can import it) before sending.
  2. Use the API's country listing (listScorableCountries / static index) to populate the picker so only rankable codes are offered.
  3. After filtering, confirm ≥2 members remain and meet all other rules; otherwise switch to a preset.
  4. Pin your client to the API version whose universe your lists were built against.

Example fix

// before
resolveBlocSelection({ members: ['US', 'VA', 'MC'] }); // throws: VA/MC not rankable
// after
const members = raw.filter((m) => isInRankableUniverse(m));
if (members.length >= 2) resolveBlocSelection({ members });
Defensive patterns

Strategy: validation

Validate before calling

const rankable = await fetchRankableUniverse(); // from listScorableCountries or static index
const members = raw.filter((m) => rankable.includes(m));
if (members.length < 2) throw new Error('not enough rankable members for a bloc');

Type guard

function isRankable(code: string, universe: ReadonlySet<string>): code is string {
  return universe.has(code);
}

Try / catch

try {
  const bloc = await getBlocScorecard({ members });
} catch (err) {
  if (err.message.includes('rankable country universe')) {
    const excluded = members.filter((m) => !universe.has(m));
    return badRequest({ excluded, hint: 'choose from listScorableCountries' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling getBlocScorecardWithReader with a custom list containing any non-rankable code: territories (e.g., 'GL', 'PR'), microstates ('MC', 'SM'), or any ISO-2 code absent from the published rankable universe, even if the rest of the list is valid.

Common situations: 1) A world country-picker lists all ISO-3166 entries including dependencies and microstates that the dataset doesn't rank; 2) universe changes between versions — a code previously rankable is dropped after a data-source revision; 3) users deliberately test edge codes ('AQ' Antarctica).

Related errors


AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-09-01). Data as JSON: /api/errors/6bf7b0fb64ac90f2. Report an issue: GitHub.