{"record":{"id":"8fc3f6a768fd0151","repo":"koala73/worldmonitor","slug":"auth-required","errorCode":"AUTH_REQUIRED","errorMessage":"AUTH_REQUIRED","messagePattern":"AUTH_REQUIRED","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/lib/auth.ts","lineNumber":63,"sourceCode":"  const identity = await ctx.auth.getUserIdentity();\n  if (identity?.subject) return identity;\n  return null;\n}\n\n/**\n * Returns the current user's ID or throws if unauthenticated.\n * Use for mutations/actions that always require auth.\n */\nexport async function requireUserId(\n  ctx: QueryCtx | MutationCtx | ActionCtx,\n): Promise<string> {\n  const userId = await resolveUserId(ctx);\n  if (!userId) {\n    // Throw as ConvexError so Convex's server-side Sentry integration treats it\n    // as an expected business error (WebSocket/auth races on query fire) rather\n    // than reporting every unauthed query fire as an unhandled exception\n    // (WORLDMONITOR-N3).\n    throw new ConvexError(\"AUTH_REQUIRED\");\n  }\n  return userId;\n}\n","sourceCodeStart":45,"sourceCodeEnd":67,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/lib/auth.ts#L45-L67","documentation":"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\"`.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before (production, no session)\nawait convex.mutation(api.apiKeys.createApiKey, { name }); // uses requireUserId internally\n\n// after — gate on auth, dev fallback for local\nconst { isSignedIn } = useAuth();\nif (!isSignedIn) { navigate(\"/sign-in\"); return; }\nawait convex.mutation(api.apiKeys.createApiKey, { name });\n\n// .env.local for local dev without Clerk\n// CONVEX_IS_DEV=true","handlingStrategy":"try-catch","validationCode":"import { useAuth } from \"@clerk/clerk-react\";\nconst { isSignedIn } = useAuth();\nif (!isSignedIn && process.env.CONVEX_IS_DEV !== \"true\") {\n  navigate(\"/sign-in\");\n  return;\n}","typeGuard":"// requireUserId honors DEV_USER_ID when CONVEX_IS_DEV=true;\n// in production there is no fallback — auth is mandatory.","tryCatchPattern":"try {\n  await convex.mutation(api.apiKeys.createApiKey, { name });\n} catch (err) {\n  if (err.data === \"AUTH_REQUIRED\") navigate(\"/sign-in\");\n  else throw err;\n}","preventionTips":["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\"."],"tags":["auth","convex","clerk","helper"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}