koala73/worldmonitor · warning

HTTP

Error message

HTTP ${response.status}

What it means

createApiKey() captures the Clerk userId up front, then calls waitForConvexAuthForUser(userId) (src/services/convex-client.ts:311), which returns false when the shared ConvexClient's auth barrier belongs to a different user, the Clerk user changed since capture, the barrier was superseded by a newer setAuth generation, or the token did not become ready within the 10s default timeout. The mutation is aborted so one account can never mint a key bound to another; a key was never generated server-side.

Solutions

  1. Retry the creation after sign-in settles; the rebind usually completes within seconds
  2. If it repeats, confirm the same user stays signed in across all tabs during the operation
  3. Check console logs for repeated auth rebind/authGeneration churn
  4. In UI, treat this message as 'please try again' rather than a dead-end error

Example fix

// before
await createApiKey(name); // 'Account changed while creating the API key. Try again.'

// after: re-check identity right before the call and retry once on this specific error
const userId = getCurrentClerkUser()?.id;
if (!userId) { openSignIn(); return; }
try {
  await createApiKey(name);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Account changed') && getCurrentClerkUser()?.id === userId) {
    await createApiKey(name); // one retry after rebind settles
  } else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

const userId = getCurrentClerkUser()?.id;
if (!userId) { openSignIn(); return; }
// re-verify right before the call so the captured identity is still current
if (getCurrentClerkUser()?.id !== userId) { abortStaleOperation(); return; }
await createApiKey(name);

Type guard

const isAccountChangedError = (e: unknown): e is Error =>
  e instanceof Error && e.message.startsWith('Account changed');

Try / catch

try {
  await createApiKey(name);
} catch (e) {
  if (isAccountChangedError(e) && getCurrentClerkUser()?.id === userId) {
    await new Promise(r => setTimeout(r, 1000));
    await createApiKey(name); // rebind usually settles within seconds
  } else throw e;
}

Prevention

When it happens

Trigger: Sign-out/sign-in as another user between capturing userId and the auth gate; sign-out mid-operation; a concurrent auth rebind (startConvexAuthRebind) replacing the barrier generation; token fetch slower than 10s.

Common situations: Users switching accounts in another tab; Clerk token refresh storms; flaky networks making the 10s barrier timeout look like an account change.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-08-21). Data as JSON: /api/errors/4bef37709fdccb61. Report an issue: GitHub.

Appendix: source

Thrown at api/fwdstart.js:39

 * terminator cannot close the section early (#7206).
 */
export function cdata(s) {
  const cleaned = String(s ?? '').replace(ILLEGAL_XML_CHARS, '');
  // `]]>` → `]]]]><![CDATA[>` so the terminator is split across sections
  // and the literal `]]>` survives when CDATA bodies are concatenated.
  return `<![CDATA[${cleaned.split(']]>').join(']]]]><![CDATA[>')}]]>`;
}

/** Fetch the archive page and extract post items. Throws on upstream failure. */
async function scrapeArchiveItems() {
  const response = await fetch('https://www.fwdstart.me/archive', {
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
      'Accept': 'text/html,application/xhtml+xml',
    },
    signal: AbortSignal.timeout(15000),
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }

  const html = await response.text();
  const items = [];
  const seenUrls = new Set();

  // Split by embla__slide to get each post block
  const slideBlocks = html.split('embla__slide');

  for (const block of slideBlocks) {
    // Extract URL
    const urlMatch = block.match(/href="(\/p\/[^"]+)"/);
    if (!urlMatch) continue;

    const url = `https://www.fwdstart.me${urlMatch[1]}`;
    if (seenUrls.has(url)) continue;

View on GitHub (pinned to 7d06c8633d)