koala73/worldmonitor · error · ConvexError
AUTH_REQUIRED
AUTH_REQUIRED
Error message
AUTH_REQUIRED
What it means
Thrown by the shared `requireUserId` helper in `convex/lib/auth.ts` when `resolveUserId` returns null — i.e. no Clerk identity AND (not in dev mode OR dev fallback disabled). This is the canonical auth gate for mutations/actions that always require a user. It is a plain-string ConvexError so `err.data === "AUTH_REQUIRED"`. It is deliberately a ConvexError (not a generic Error) so Convex's server-side Sentry integration treats it as an expected business error rather than reporting every unauthed query fire as an unhandled exception (WORLDMONITOR-N3). Unlike `followedCountries` which calls `getUserIdentity()` directly, this helper DOES honor the dev fallback (`DEV_USER_ID = "test-user-001"`) when `CONVEX_IS_DEV === "true"`.
Source
Thrown at convex/lib/auth.ts:63
const identity = await ctx.auth.getUserIdentity();
if (identity?.subject) return identity;
return null;
}
/**
* Returns the current user's ID or throws if unauthenticated.
* Use for mutations/actions that always require auth.
*/
export async function requireUserId(
ctx: QueryCtx | MutationCtx | ActionCtx,
): Promise<string> {
const userId = await resolveUserId(ctx);
if (!userId) {
// Throw as ConvexError so Convex's server-side Sentry integration treats it
// as an expected business error (WebSocket/auth races on query fire) rather
// than reporting every unauthed query fire as an unhandled exception
// (WORLDMONITOR-N3).
throw new ConvexError("AUTH_REQUIRED");
}
return userId;
}
View on GitHub (pinned to ffec79ac33)
Solutions
- Gate the calling UI on `useAuth().isSignedIn` before invoking the mutation.
- For local dev without Clerk, set `CONVEX_IS_DEV=true` in `.env.local` so `requireUserId` returns `DEV_USER_ID`.
- On `err.data === "AUTH_REQUIRED"`, redirect to sign-in and retry the action after re-auth.
- Never infer dev mode from a missing env var — explicitly set `CONVEX_IS_DEV=true`; otherwise production could silently fall back to the test user.
Example fix
// before (production, no session)
await convex.mutation(api.apiKeys.createApiKey, { name }); // uses requireUserId internally
// after — gate on auth, dev fallback for local
const { isSignedIn } = useAuth();
if (!isSignedIn) { navigate("/sign-in"); return; }
await convex.mutation(api.apiKeys.createApiKey, { name });
// .env.local for local dev without Clerk
// CONVEX_IS_DEV=true Defensive patterns
Strategy: try-catch
Validate before calling
import { useAuth } from "@clerk/clerk-react";
const { isSignedIn } = useAuth();
if (!isSignedIn && process.env.CONVEX_IS_DEV !== "true") {
navigate("/sign-in");
return;
} Type guard
// requireUserId honors DEV_USER_ID when CONVEX_IS_DEV=true; // in production there is no fallback — auth is mandatory.
Try / catch
try {
await convex.mutation(api.apiKeys.createApiKey, { name });
} catch (err) {
if (err.data === "AUTH_REQUIRED") navigate("/sign-in");
else throw err;
} Prevention
- Gate UI on `isSignedIn` before calling any mutation using requireUserId.
- Set CONVEX_IS_DEV=true in .env.local for local dev without Clerk.
- Never infer dev mode from a missing env var — set it explicitly.
- This is a plain-string ConvexError: branch on err.data === "AUTH_REQUIRED".
When it happens
Trigger: Any mutation/action using `requireUserId` is called without a Clerk session; the session expired; in production (`CONVEX_IS_DEV` unset/false) with no auth header; calling from a context that doesn't propagate the Convex auth token.
Common situations: Session expired on a long-open tab; a mutation fired from a logged-out state; a background scheduler/action invoked without a user context; production deploy where the dev fallback correctly does NOT apply (by design — `isDev` is derived only from `CONVEX_IS_DEV`, never from a missing env var, so production never accidentally behaves as dev).
Related errors
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/8fc3f6a768fd0151.
Report an issue: GitHub.