{"record":{"id":"cdc9d2e063f6c9be","repo":"koala73/worldmonitor","slug":"too-many-origins","errorCode":"TOO_MANY_ORIGINS","errorMessage":"TOO_MANY_ORIGINS","messagePattern":"TOO_MANY_ORIGINS","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/embedKeys.ts","lineNumber":34,"sourceCode":" * Validation only — nothing enforces these at request time (see schema.ts).\n */\nfunction normalizeAllowedOrigins(origins: string[] | undefined): string[] | undefined {\n  if (origins === undefined) return undefined;\n  const normalized = new Set<string>();\n  for (const raw of origins) {\n    const value = raw.trim();\n    if (!value) continue;\n    let origin: string;\n    try {\n      origin = new URL(value).origin;\n    } catch {\n      throw new ConvexError(\"INVALID_ORIGIN\");\n    }\n    if (origin !== value) throw new ConvexError(\"INVALID_ORIGIN\");\n    normalized.add(origin);\n  }\n  if (normalized.size === 0) return undefined;\n  if (normalized.size > MAX_ALLOWED_ORIGINS) throw new ConvexError(\"TOO_MANY_ORIGINS\");\n  return [...normalized].sort();\n}\n\n// ---------------------------------------------------------------------------\n// Public mutations & queries (require Clerk JWT via ctx.auth)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a new partner-embed key.\n *\n * Same shown-once discipline as `convex/apiKeys.ts`: the caller generates the\n * random key client-side and passes the SHA-256 hex hash + the display prefix.\n * The plaintext key is NEVER stored in Convex.\n *\n * The gate is the shared account embed predicate — a verified Clerk PRO role\n * or active paid embed entitlement — NOT `apiAccess`. An embed key is\n * published in the partner's HTML, so it must be mintable by every paid tier;\n * Pro and Pro Business are `apiAccess: false` and `createApiKey` rejects them.","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/convex/embedKeys.ts#L16-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reduce the list to 10 or fewer distinct origins before calling the mutation.","Deduplicate client-side with `new Set(origins)` — duplicates are free, only unique origins count.","Consolidate subdomains onto one registrable domain where your embed usage permits, or split usage across multiple embed keys.","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."],"exampleFix":"// before\nconst origins = [\"https://a.example\", \"https://a.example\", \"https://b.example\", /* ...14 more */];\nawait api.embedKeys.createEmbedKey({ name, keyPrefix, keyHash, allowedOrigins: origins });\n// after\nconst origins = [...new Set(allOrigins)].slice(0, 10);\nif (new Set(allOrigins).size > 10) throw new Error(\"Too many origins: max 10 unique\");\nawait api.embedKeys.createEmbedKey({ name, keyPrefix, keyHash, allowedOrigins: origins });","handlingStrategy":"validation","validationCode":"const unique = [...new Set((allowedOrigins ?? []).map(o => o.trim()).filter(Boolean))];\nif (unique.length > 10) throw new Error(`Max 10 unique origins, got ${unique.length}`);","typeGuard":null,"tryCatchPattern":"try {\n  await api.embedKeys.createEmbedKey({ ...args, allowedOrigins });\n} catch (e) {\n  if (e instanceof ConvexError && e.data === \"TOO_MANY_ORIGINS\") {\n    // ask the user to prune the list to 10 distinct origins\n  } else throw e;\n}","preventionTips":["Deduplicate with Set before sending; duplicates are free, unique origins count","Cap origin pickers in the UI at 10 entries","Don't bulk-import preview/staging deploy URLs into embed keys"],"tags":["convex","validation","limit-exceeded","embed-keys"],"backgroundTag":"value-out-of-range","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}