koala73/worldmonitor · error · ConvexError

UNAUTHENTICATED

UNAUTHENTICATED

Error message

UNAUTHENTICATED

What it means

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.

Source

Thrown at convex/alertRules.ts:209

export const setAlertRules = mutation({
  args: {
    variant: v.string(),
    enabled: v.boolean(),
    eventTypes: v.array(v.string()),
    sensitivity: v.optional(sensitivityValidator),
    channels: v.array(channelTypeValidator),
    aiDigestEnabled: v.optional(v.boolean()),
    // Optional country-scope (ISO-3166 alpha-2). Omit to preserve existing.
    // Pass [] to explicitly reset to "all countries". Pass [...] to restrict.
    countries: v.optional(v.array(v.string())),
    // Optional watchlist ticker-scope (#4922 U3). Omit to preserve existing;
    // [] resets. Unlike countries, [] means "no watchlist story alerts".
    tickers: v.optional(v.array(v.string())),
  },
  handler: async (ctx, args) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) throw new ConvexError("UNAUTHENTICATED");
    const userId = identity.subject;
    await assertProEntitlement(ctx, userId);

    const existing = await ctx.db
      .query("alertRules")
      .withIndex("by_user_variant", (q) =>
        q.eq("userId", userId).eq("variant", args.variant),
      )
      .unique();

    const pair = resolveEffectivePair({
      incomingSensitivity: args.sensitivity,
      existing: existing ?? undefined,
    });
    assertCompatibleDeliveryMode(pair);

    const normalizedCountries = args.countries !== undefined
      ? normalizeCountries(args.countries)

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Ensure the Convex client has a valid Clerk session before invoking the mutation (await auth state readiness).
  2. Gate the UI call site behind a signed-in check so the mutation is only called when isAuthenticated is true.
  3. On receipt of this error, route the user to sign-in rather than retrying blindly.

Example fix

// before
useMutation(api.alertRules.setAlertRules)(args); // fires before auth resolves
// after
const { isSignedIn } = useUser();
const setAlertRules = useMutation(api.alertRules.setAlertRules);
if (isSignedIn) setAlertRules(args);
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure auth is resolved before calling the mutation.
const identity = await convex.auth.getUserIdentity();
if (!identity) {
  // route to sign-in
  return;
}
await setAlertRules(args);

Type guard

function isSignedIn(user): user is { subject: string } {
  return !!user && typeof user.subject === 'string';
}

Try / catch

try {
  await setAlertRules(args);
} catch (e) {
  if (e instanceof ConvexError && e.message === 'UNAUTHENTICATED') {
    // session expired — re-authenticate, do not blind-retry
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/1bdfadb9895cc430. Report an issue: GitHub.