{"record":{"id":"59c1410c702f6645","repo":"koala73/worldmonitor","slug":"account-changed-while-revoking-the-embed-key-try-again","errorCode":null,"errorMessage":"Account changed while revoking the embed key. Try again.","messagePattern":"Account changed while revoking the embed key\\. Try again\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/embed-keys.ts","lineNumber":146,"sourceCode":" * Revoke an embed key by its Convex document ID.\n *\n * Unlike `revokeApiKey`, this does not bust the edge validation cache: there is\n * no ownership-checked invalidation route for `embedKeys` yet, so a revoked key\n * keeps validating for at most the 60s `CACHE_TTL_SECONDS` in\n * `server/_shared/embed-key.ts`.\n *\n * A map frame is slower still: it already holds a `wmg_` grant good for up to\n * `EMBED_GRANT_TTL_MS` (30 minutes), and revocation only stops the NEXT mint.\n * The UI copy states both windows rather than promising one.\n */\nexport async function revokeEmbedKey(keyId: string): Promise<void> {\n  const userId = getCurrentClerkUser()?.id;\n  if (!userId) throw new Error('Sign in to revoke embed keys.');\n\n  const [client, api] = await Promise.all([getConvexClient(), getConvexApi()]);\n  if (!client || !api) throw new Error('Convex unavailable');\n  if (!await waitForConvexAuthForUser(userId)) {\n    throw new Error('Account changed while revoking the embed key. Try again.');\n  }\n\n  await settleAccountOperation(\n    userId,\n    'revoking the embed key',\n    () => client.mutation((api as any).embedKeys.revokeEmbedKey, { keyId }),\n  );\n  assertAccountStillCurrent(userId, 'revoking the embed key');\n}\n","sourceCodeStart":128,"sourceCodeEnd":156,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/src/services/embed-keys.ts#L128-L156","documentation":"After obtaining the Convex client, revokeEmbedKey awaits waitForConvexAuthForUser(userId); if auth does not confirm for that exact user within the window it throws this error. It means the authenticated identity changed (or never settled) between reading the user and issuing the mutation — a guard against revoking keys under the wrong account.","triggerScenarios":"Calling revokeEmbedKey() and the Clerk account switches (sign-out, sign-in as another user, session token rotation failing) while waiting for Convex auth to settle for the original userId.","commonSituations":"User A clicks revoke, then the tab completes a sign-in as user B; session expiry mid-action; multi-tab account switching; slow Convex auth causing the wait to time out right as the user changes accounts.","solutions":["Simply retry after the account situation settles — the message explicitly says 'Try again'","Re-read getCurrentClerkUser() after the failure; if the user id changed, re-run the action in the new session only if that user owns the key","Avoid switching accounts while a destructive action is in flight; disable action buttons during pending auth transitions"],"exampleFix":"// before\nawait revokeEmbedKey(keyId);\n// after\ntry {\n  await revokeEmbedKey(keyId);\n} catch (e) {\n  if (/Account changed/.test(e.message)) {\n    showToast('Your account changed. Please retry the revocation.');\n  } else throw e;\n}","handlingStrategy":"retry","validationCode":"const uid = getCurrentClerkUser()?.id; if (!uid) return;","typeGuard":null,"tryCatchPattern":"try { await revokeEmbedKey(keyId); } catch (e) { if (/Account changed/.test(e.message)) { promptRetryAfterAccountSettles(); } else throw e; }","preventionTips":["Suspend destructive actions during account transitions","Re-verify the user id after any auth-affecting await","Keep multi-tab account switching in mind when automating"],"tags":["authentication","convex","race-condition","account-switch"],"backgroundTag":"invalid-state-transition","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}