koala73/worldmonitor · error

Sign in to revoke embed keys.

Error message

Sign in to revoke embed keys.

What it means

revokeEmbedKey(keyId) requires an authenticated user: it reads the current Clerk user id, and if there is none it throws this error before any Convex call. Unlike the read path (listEmbedKeys returns []), a destructive mutation refuses to silently no-op when signed out.

Solutions

  1. Check getCurrentClerkUser() for a non-null user before offering/invoking revocation, and route to sign-in instead
  2. Sign in with Clerk, then retry the revocation
  3. If the session just expired, refresh the page so Clerk rehydrates before retrying

Example fix

// before
await revokeEmbedKey(keyId);
// after
if (!getCurrentClerkUser()) {
  openSignIn();
  return;
}
await revokeEmbedKey(keyId);
Defensive patterns

Strategy: validation

Validate before calling

if (!getCurrentClerkUser()) { openSignIn(); return; }

Type guard

const isSignedIn = (): boolean => getCurrentClerkUser() != null;

Prevention

When it happens

Trigger: Calling revokeEmbedKey(keyId) when getCurrentClerkUser() returns undefined — i.e. the visitor is signed out, or the Clerk session has been lost/expired at the moment of the call.

Common situations: Clicking 'revoke' on an embed key after the Clerk session expired in a long-lived tab; calling the API from code that runs before Clerk finishes initializing; a signed-out state after session revocation on another device.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/09b0375467cbc885. Report an issue: GitHub.

Appendix: source

Thrown at src/services/embed-keys.ts:141

    () => client.query((api as any).embedKeys.listEmbedKeys, {}),
  );
}

/**
 * Revoke an embed key by its Convex document ID.
 *
 * Unlike `revokeApiKey`, this does not bust the edge validation cache: there is
 * no ownership-checked invalidation route for `embedKeys` yet, so a revoked key
 * keeps validating for at most the 60s `CACHE_TTL_SECONDS` in
 * `server/_shared/embed-key.ts`.
 *
 * A map frame is slower still: it already holds a `wmg_` grant good for up to
 * `EMBED_GRANT_TTL_MS` (30 minutes), and revocation only stops the NEXT mint.
 * The UI copy states both windows rather than promising one.
 */
export async function revokeEmbedKey(keyId: string): Promise<void> {
  const userId = getCurrentClerkUser()?.id;
  if (!userId) throw new Error('Sign in to revoke embed keys.');

  const [client, api] = await Promise.all([getConvexClient(), getConvexApi()]);
  if (!client || !api) throw new Error('Convex unavailable');
  if (!await waitForConvexAuthForUser(userId)) {
    throw new Error('Account changed while revoking the embed key. Try again.');
  }

  await settleAccountOperation(
    userId,
    'revoking the embed key',
    () => client.mutation((api as any).embedKeys.revokeEmbedKey, { keyId }),
  );
  assertAccountStillCurrent(userId, 'revoking the embed key');
}

View on GitHub (pinned to 7d06c8633d)