{"record":{"id":"1bdfadb9895cc430","repo":"koala73/worldmonitor","slug":"unauthenticated","errorCode":"UNAUTHENTICATED","errorMessage":"UNAUTHENTICATED","messagePattern":"UNAUTHENTICATED","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/alertRules.ts","lineNumber":209,"sourceCode":"\nexport const setAlertRules = mutation({\n  args: {\n    variant: v.string(),\n    enabled: v.boolean(),\n    eventTypes: v.array(v.string()),\n    sensitivity: v.optional(sensitivityValidator),\n    channels: v.array(channelTypeValidator),\n    aiDigestEnabled: v.optional(v.boolean()),\n    // Optional country-scope (ISO-3166 alpha-2). Omit to preserve existing.\n    // Pass [] to explicitly reset to \"all countries\". Pass [...] to restrict.\n    countries: v.optional(v.array(v.string())),\n    // Optional watchlist ticker-scope (#4922 U3). Omit to preserve existing;\n    // [] resets. Unlike countries, [] means \"no watchlist story alerts\".\n    tickers: v.optional(v.array(v.string())),\n  },\n  handler: async (ctx, args) => {\n    const identity = await ctx.auth.getUserIdentity();\n    if (!identity) throw new ConvexError(\"UNAUTHENTICATED\");\n    const userId = identity.subject;\n    await assertProEntitlement(ctx, userId);\n\n    const existing = await ctx.db\n      .query(\"alertRules\")\n      .withIndex(\"by_user_variant\", (q) =>\n        q.eq(\"userId\", userId).eq(\"variant\", args.variant),\n      )\n      .unique();\n\n    const pair = resolveEffectivePair({\n      incomingSensitivity: args.sensitivity,\n      existing: existing ?? undefined,\n    });\n    assertCompatibleDeliveryMode(pair);\n\n    const normalizedCountries = args.countries !== undefined\n      ? normalizeCountries(args.countries)","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/alertRules.ts#L191-L227","documentation":"Thrown at the top of the setAlertRules mutation handler when ctx.auth.getUserIdentity() returns null — i.e. no authenticated Clerk session. The message is the bare string 'UNAUTHENTICATED' (no structured code), surfaced as a ConvexError so the client receives it instead of a generic 500.","triggerScenarios":"Calling the setAlertRules Convex mutation without a valid Clerk JWT in the auth context; calling after session expiry but before token refresh; calling from a context where the Convex client was not configured with an auth provider.","commonSituations":"Token-expired race during sign-out/sign-in transitions; a page that mounts the mutation call before the auth provider has resolved; misconfigured ConvexReactClient missing setAuth(); a bot or script hitting the mutation without credentials.","solutions":["Ensure the Convex client has a valid Clerk session before invoking the mutation (await auth state readiness).","Gate the UI call site behind a signed-in check so the mutation is only called when isAuthenticated is true.","On receipt of this error, route the user to sign-in rather than retrying blindly."],"exampleFix":"// before\nuseMutation(api.alertRules.setAlertRules)(args); // fires before auth resolves\n// after\nconst { isSignedIn } = useUser();\nconst setAlertRules = useMutation(api.alertRules.setAlertRules);\nif (isSignedIn) setAlertRules(args);","handlingStrategy":"type-guard","validationCode":"// Ensure auth is resolved before calling the mutation.\nconst identity = await convex.auth.getUserIdentity();\nif (!identity) {\n  // route to sign-in\n  return;\n}\nawait setAlertRules(args);","typeGuard":"function isSignedIn(user): user is { subject: string } {\n  return !!user && typeof user.subject === 'string';\n}","tryCatchPattern":"try {\n  await setAlertRules(args);\n} catch (e) {\n  if (e instanceof ConvexError && e.message === 'UNAUTHENTICATED') {\n    // session expired — re-authenticate, do not blind-retry\n  } else throw e;\n}","preventionTips":["Gate every setAlertRules call site behind useUser().isSignedIn.","Await Convex auth readiness on mount before enabling the submit action.","On UNAUTHENTICATED, route to sign-in rather than retrying in a loop."],"tags":["convex","auth","clerk","notifications"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}