koala73/worldmonitor · error · ConvexError

TOO_MANY_ORIGINS

TOO_MANY_ORIGINS

Error message

TOO_MANY_ORIGINS

What it means

After trimming, deduping, and validating origins, normalizeAllowedOrigins rejects any set larger than MAX_ALLOWED_ORIGINS (10). The cap exists because embed keys declare a partner's own sites, not a CDN or wildcard proxy list. ConvexError("TOO_MANY_ORIGINS") is thrown before any database write occurs.

Solutions

  1. Reduce the list to 10 or fewer distinct origins before calling the mutation.
  2. Deduplicate client-side with `new Set(origins)` — duplicates are free, only unique origins count.
  3. Consolidate subdomains onto one registrable domain where your embed usage permits, or split usage across multiple embed keys.
  4. If the use case genuinely needs more origins, it conflicts with the product cap; revoke unused keys or reconsider the origin strategy rather than retrying.

Example fix

// before
const origins = ["https://a.example", "https://a.example", "https://b.example", /* ...14 more */];
await api.embedKeys.createEmbedKey({ name, keyPrefix, keyHash, allowedOrigins: origins });
// after
const origins = [...new Set(allOrigins)].slice(0, 10);
if (new Set(allOrigins).size > 10) throw new Error("Too many origins: max 10 unique");
await api.embedKeys.createEmbedKey({ name, keyPrefix, keyHash, allowedOrigins: origins });
Defensive patterns

Strategy: validation

Validate before calling

const unique = [...new Set((allowedOrigins ?? []).map(o => o.trim()).filter(Boolean))];
if (unique.length > 10) throw new Error(`Max 10 unique origins, got ${unique.length}`);

Try / catch

try {
  await api.embedKeys.createEmbedKey({ ...args, allowedOrigins });
} catch (e) {
  if (e instanceof ConvexError && e.data === "TOO_MANY_ORIGINS") {
    // ask the user to prune the list to 10 distinct origins
  } else throw e;
}

Prevention

When it happens

Trigger: Calling createEmbedKey or updateEmbedKey with allowedOrigins containing more than 10 distinct valid origins. Duplicates do not count — the check is on the deduplicated Set size, so 12 entries with 3 duplicates (size 9) pass while 11 unique origins fail.

Common situations: Bulk-importing a large allowlist of staging, preview, and production domains; passing every Vercel/Netlify preview deployment URL; scripting key creation across many customers with a shared oversized template list.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/cdc9d2e063f6c9be. Report an issue: GitHub.

Appendix: source

Thrown at convex/embedKeys.ts:34

 * Validation only — nothing enforces these at request time (see schema.ts).
 */
function normalizeAllowedOrigins(origins: string[] | undefined): string[] | undefined {
  if (origins === undefined) return undefined;
  const normalized = new Set<string>();
  for (const raw of origins) {
    const value = raw.trim();
    if (!value) continue;
    let origin: string;
    try {
      origin = new URL(value).origin;
    } catch {
      throw new ConvexError("INVALID_ORIGIN");
    }
    if (origin !== value) throw new ConvexError("INVALID_ORIGIN");
    normalized.add(origin);
  }
  if (normalized.size === 0) return undefined;
  if (normalized.size > MAX_ALLOWED_ORIGINS) throw new ConvexError("TOO_MANY_ORIGINS");
  return [...normalized].sort();
}

// ---------------------------------------------------------------------------
// Public mutations & queries (require Clerk JWT via ctx.auth)
// ---------------------------------------------------------------------------

/**
 * Create a new partner-embed key.
 *
 * Same shown-once discipline as `convex/apiKeys.ts`: the caller generates the
 * random key client-side and passes the SHA-256 hex hash + the display prefix.
 * The plaintext key is NEVER stored in Convex.
 *
 * The gate is the shared account embed predicate — a verified Clerk PRO role
 * or active paid embed entitlement — NOT `apiAccess`. An embed key is
 * published in the partner's HTML, so it must be mintable by every paid tier;
 * Pro and Pro Business are `apiAccess: false` and `createApiKey` rejects them.

View on GitHub (pinned to 7d06c8633d)