koala73/worldmonitor · warning

Authentication unavailable while loading embed keys. Try aga

Error message

Authentication unavailable while loading embed keys. Try again.

What it means

listEmbedKeys() returns [] when signed out but throws this when the user is signed in yet Clerk/Convex auth never becomes ready within the wait window (waitForConvexAuthForUser(userId) resolved false). It signals a transient auth-hydration stall rather than a data problem; assertAccountStillCurrent runs first so an account switch throws its own message instead.

Solutions

  1. Retry the call after a short delay; the error message itself says 'Try again' and the condition is usually transient
  2. Ensure the component only calls listEmbedKeys() after Clerk/Convex auth is confirmed ready (await an auth-ready hook/gate) instead of on mount
  3. Check network connectivity and Convex deployment availability; a blocked Convex websocket keeps auth pending
  4. If persistent, sign out and back in to reset the Clerk session and Convex auth client

Example fix

// before
const keys = await listEmbedKeys();
// after
let keys;
try {
  keys = await listEmbedKeys();
} catch (e) {
  if (e.message.includes('Authentication unavailable')) {
    keys = await retry(listEmbedKeys, { retries: 3, delayMs: 500 });
  } else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

if (!getCurrentClerkUser()) return []; // signed out: listEmbedKeys returns [] anyway

Try / catch

try { keys = await listEmbedKeys(); } catch (e) { if (/Authentication unavailable/.test(e.message)) { await authReady(); keys = await listEmbedKeys(); } else throw e; }

Prevention

When it happens

Trigger: Calling listEmbedKeys() while the Clerk session/Convex token is still initializing, or when waitForConvexAuthForUser times out because Convex auth never completes for the current userId.

Common situations: Opening the embed-keys settings panel immediately after a page load or sign-in; slow network delaying the Convex auth handshake; a stale session rehydrating; switching accounts mid-load so the awaited user no longer matches.

Understand the failure class

Related errors


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

Appendix: source

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

      (api as any).embedKeys.createEmbedKey,
      { name: name.trim(), keyPrefix, keyHash },
    ),
  );
  assertAccountStillCurrent(userId, 'creating the embed key');

  return { id: result.id, name: result.name, keyPrefix: result.keyPrefix, key: plaintext };
}

/** List all embed keys for the current user. */
export async function listEmbedKeys(): Promise<EmbedKeyInfo[]> {
  const userId = getCurrentClerkUser()?.id;
  if (!userId) return [];

  const [client, api] = await Promise.all([getConvexClient(), getConvexApi()]);
  if (!client || !api) return [];
  if (!await waitForConvexAuthForUser(userId)) {
    assertAccountStillCurrent(userId, 'loading embed keys');
    throw new Error('Authentication unavailable while loading embed keys. Try again.');
  }

  return settleAccountOperation(
    userId,
    'loading embed keys',
    () => 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

View on GitHub (pinned to 7d06c8633d)