koala73/worldmonitor · error · ConvexError
COUNTRIES_LIMIT_EXCEEDED
COUNTRIES_LIMIT_EXCEEDED
Error message
countries list capped at ${COUNTRIES_MAX} entries What it means
Thrown by normalizeCountries when the cleaned/deduped country list exceeds COUNTRIES_MAX (50). Input is trimmed, uppercased, filtered to ^[A-Z]{2}$ shape, and deduped before the cap is checked, so only valid unique ISO-3166 alpha-2 codes count toward the limit. The cap is a defensive ceiling against patched-client abuse, not an ISO registry check.
Source
Thrown at convex/alertRules.ts:129
* NOT a strict ISO-3166 registry check — invalid alpha-2 codes (e.g. "XX")
* pass shape validation but the relay's includes() check just won't match
* any real event country. We deliberately don't soft-couple this file to a
* canonical registry list (that lives elsewhere as part of the
* followed-countries primitive) — keep alertRules independently shippable.
*/
function normalizeCountries(input: string[]): string[] {
const cleaned: string[] = [];
const seen = new Set<string>();
for (const raw of input) {
if (typeof raw !== "string") continue;
const upper = raw.trim().toUpperCase();
if (!/^[A-Z]{2}$/.test(upper)) continue;
if (seen.has(upper)) continue;
seen.add(upper);
cleaned.push(upper);
}
if (cleaned.length > COUNTRIES_MAX) {
throw new ConvexError({
code: "COUNTRIES_LIMIT_EXCEEDED",
message: `countries list capped at ${COUNTRIES_MAX} entries`,
});
}
return cleaned;
}
// Same defensive ceiling as COUNTRIES_MAX — mirrors the client-side market
// watchlist cap (src/services/market-watchlist.ts stops at 50 entries).
const TICKERS_MAX = 50;
/**
* Shape-validate + normalize an inbound `tickers` array (#4922 U3).
* Modeled EXACTLY on normalizeCountries above:
* - trim each entry
* - uppercase
* - keep only `^[A-Z][A-Z0-9&-]{0,11}(\.[A-Z]{1,3})?$` shapes — plain
* symbols (AAPL), share-class/conglomerate forms (BRK-B, M&M.NS) andView on GitHub (pinned to ffec79ac33)
Solutions
- Reduce the countries array to at most 50 unique ISO-3166 alpha-2 codes before submitting.
- Drop invalid/duplicate entries client-side before the call (they waste nothing but make the count hard to reason about).
- If a legitimate need exceeds 50, reconsider the scope — the comment notes ~250 countries exist and 50 is already generous.
Example fix
// before
const countries = allWorldCountries; // 200+ entries
await setAlertRules(ctx, { ..., countries });
// after
const countries = prioritizeCountries(allWorldCountries).slice(0, 50);
await setAlertRules(ctx, { ..., countries }); Defensive patterns
Strategy: validation
Validate before calling
const COUNTRIES_MAX = 50;
function normalizeAndCapCountries(input) {
const seen = new Set();
const out = [];
for (const raw of input) {
const upper = String(raw).trim().toUpperCase();
if (!/^[A-Z]{2}$/.test(upper) || seen.has(upper)) continue;
seen.add(upper);
out.push(upper);
if (out.length === COUNTRIES_MAX) break;
}
return out;
} Type guard
function isCountryList(input): input is string[] {
return Array.isArray(input) && input.every(c => typeof c === 'string' && /^[A-Z]{2}$/.test(c.trim().toUpperCase()));
} Try / catch
try {
await setAlertRules(args);
} catch (e) {
if (e instanceof ConvexError && /countries list capped/.test(String(e.message))) {
// truncate and retry, or surface to user
} else throw e;
} Prevention
- Client-side, mirror normalizeCountries exactly (trim, uppercase, ^[A-Z]{2}$ filter, dedupe) before submit.
- Hard-cap the picker UI at 50 selections.
- Reconcile against the same COUNTRIES_MAX constant used server-side.
When it happens
Trigger: Calling setAlertRules, setAlertRulesForUser, setDigestSettingsForUser, setQuietHoursForUser, or setNotificationConfigForUser with a countries array containing more than 50 unique valid alpha-2 codes after normalization.
Common situations: A client patched to bypass UI limits submitting a bulk import; a migration script carrying an oversized country list; a watchlist sync that concatenates multiple region lists.
Related errors
- TICKERS_LIMIT_EXCEEDED
- INCOMPATIBLE_DELIVERY
- digestHour must be an integer 0–23
- digestTimezone must be a valid IANA timezone (e.g. America/N
- quietHoursStart must be an integer 0–23
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/bb6825c40d4e9b93.
Report an issue: GitHub.