koala73/worldmonitor · error

Sign in to create an embed key.

Error message

Sign in to create an embed key.

What it means

createEmbedKey requires an authenticated Clerk user; it reads getCurrentClerkUser()?.id and throws this error when there is none. The key creation flow (generate plaintext, hash, Convex mutation) is always scoped to a user, so an anonymous session cannot proceed.

Solutions

  1. Sign in via Clerk before attempting to create an embed key.
  2. Gate the embed-key creation UI on the Clerk auth state (show a sign-in prompt until isSignedIn).
  3. Await Clerk session hydration (useUser/isLoaded equivalent) before invoking createEmbedKey.
  4. Verify the Clerk provider wraps the component so getCurrentClerkUser() can resolve.

Example fix

// before
await createEmbedKey(name);
// after
const { isSignedIn, isLoaded } = useClerkUser();
if (!isLoaded) return; // wait for hydration
if (!isSignedIn) { showSignInPrompt(); return; }
await createEmbedKey(name);
Defensive patterns

Strategy: validation

Validate before calling

const userId = getCurrentClerkUser()?.id;
if (!userId) { showSignInPrompt(); return; }
await createEmbedKey(name);

Type guard

const isSignedIn = (): boolean => typeof getCurrentClerkUser()?.id === 'string';

Try / catch

try {
  await createEmbedKey(name);
} catch (e) {
  if (e.message === 'Sign in to create an embed key.') showSignInModal();
  else throw e;
}

Prevention

When it happens

Trigger: Calling createEmbedKey(name) while the user is signed out, the Clerk session has not yet loaded (user is null), or the Clerk provider failed to initialize so no user id is available.

Common situations: Embed-key UI reachable before Clerk finishes loading its session; user's session expired and was silently signed out; embedding the app without the Clerk provider configured; calling createEmbedKey from code that runs before auth hydration.

Related errors


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

Appendix: source

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

/**
 * The display prefix stored alongside the hash.
 *
 * Nine characters — `wme_` plus five hex — because Convex validates it against
 * `/^wme_[a-f0-9]{5}$/`. `api-keys.ts` slices eight for `wm_` + five; the
 * longer scheme prefix is the whole difference, so this cannot be shared as a
 * constant without one of the two silently taking the other's length.
 */
function embedKeyPrefix(plaintext: string): string {
  return plaintext.slice(0, 9);
}

/**
 * Create a new embed key for the current user.
 * Returns the full plaintext key (shown once) and metadata.
 */
export async function createEmbedKey(name: string): Promise<CreateEmbedKeyResult> {
  const userId = getCurrentClerkUser()?.id;
  if (!userId) throw new Error('Sign in to create an embed key.');

  const plaintext = generateEmbedKey();
  const keyPrefix = embedKeyPrefix(plaintext);
  const keyHash = await sha256Hex(plaintext);

  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 creating the embed key. Try again.');
  }

  const result = await settleAccountOperation(
    userId,
    'creating the embed key',
    () => client.mutation(
      (api as any).embedKeys.createEmbedKey,
      { name: name.trim(), keyPrefix, keyHash },
    ),

View on GitHub (pinned to 7d06c8633d)