{"record":{"id":"ff1f42b9493b25d4","repo":"koala73/worldmonitor","slug":"empty-input","errorCode":"EMPTY_INPUT","errorMessage":"EMPTY_INPUT","messagePattern":"EMPTY_INPUT","errorType":"validation","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/followedCountries.ts","lineNumber":497,"sourceCode":" *\n * Resolves Codex-deepening round-1 P0 (server-side cap on merge) and\n * round-2 P1 (canonicalize duplicates before counting). Free users with\n * existingCount >= LIMIT accept zero new rows — never silently grow above\n * the cap during merge. (Grandfathering above-cap rows on PRO→free\n * downgrade is a separate concern handled by NOT auto-deleting on\n * downgrade; merge is the FIRST sign-in and has no PRO history to\n * grandfather.)\n */\nexport const mergeAnonymousLocal = mutation({\n  args: { countries: v.array(v.string()) },\n  handler: async (ctx, args): Promise<MergeAnonymousLocalResult> => {\n    const identity = await ctx.auth.getUserIdentity();\n    if (!identity) throw new ConvexError({ kind: \"UNAUTHENTICATED\" });\n    const userId = identity.subject;\n\n    // Step 2: empty-input guard.\n    if (args.countries.length === 0) {\n      throw new ConvexError({ kind: \"EMPTY_INPUT\" });\n    }\n\n    // Step 3: defensive upper-bound on input length.\n    if (args.countries.length > MAX_MERGE_INPUT) {\n      throw new ConvexError({\n        kind: \"INPUT_TOO_LARGE\",\n        max: MAX_MERGE_INPUT,\n        received: args.countries.length,\n      });\n    }\n\n    // Step 4: ISO-2 registry filter; collect droppedInvalid in input order.\n    const droppedInvalid: string[] = [];\n    const validInputs: string[] = [];\n    for (const code of args.countries) {\n      if (isValidIso2(code)) {\n        validInputs.push(code);\n      } else {","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/convex/followedCountries.ts#L479-L515","documentation":"Guard in mergeAnonymousLocal: args.countries is an empty array, so there is nothing to merge from the anonymous local state. Thrown as a typed ConvexError so callers can skip the merge instead of performing a pointless authenticated mutation.","triggerScenarios":"Thrown at convex/followedCountries.ts:497 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Skip calling mergeAnonymousLocal when the local countries list is empty","Catch EMPTY_INPUT client-side and treat it as a no-op success"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}