Stirling-Tools/Stirling-PDF · error · Error

errorData.error || Failed to synchronize user upgrade

Error message

errorData.error || Failed to synchronize user upgrade

What it means

Thrown by synchronizeUserUpgrade() when the POST /api/v1/user-role/promptToAuthUser backend endpoint returns a non-OK HTTP status. The function attempts to parse errorData.error from the JSON body, falling back to a generic message if parsing fails. This endpoint upgrades an anonymous user to an authenticated user in the backend's security context — the backend derives the user from the session cookie, not the request body.

Source

Thrown at frontend/editor/src/saas/services/userService.ts:41

  const formData = new URLSearchParams();
  if (authMethod) {
    formData.append("authMethod", authMethod);
  }

  const response = await fetch(`${API_BASE}/user-role/promptToAuthUser`, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
    credentials: "include", // Include cookies for authentication
    body: formData.toString(),
  });

  if (!response.ok) {
    const errorData = await response
      .json()
      .catch(() => ({ error: "Failed to synchronize user upgrade" }));
    throw new Error(errorData.error || "Failed to synchronize user upgrade");
  }

  return response.json();
};

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Read errorData.error for the backend's specific error message
  2. Verify the session cookie is still valid — re-authenticate via Supabase and retry
  3. Check backend logs for /user-role/promptToAuthUser exceptions
  4. Ensure credentials:'include' is actually sending the cookie (check CORS Allow-Credentials header on the backend)
  5. Retry the sync after confirming the backend is healthy

Example fix

// before
if (!response.ok) {
  const errorData = await response.json().catch(() => ({ error: "Failed to synchronize user upgrade" }));
  throw new Error(errorData.error || "Failed to synchronize user upgrade");
}

// after
if (!response.ok) {
  const errorData = await response.json().catch(() => ({ error: "Failed to synchronize user upgrade" }));
  if (response.status === 401 || response.status === 403) {
    // Session expired — re-auth and retry once
    await refreshSession();
    return synchronizeUserUpgrade(authMethod);
  }
  throw new Error(errorData.error || "Failed to synchronize user upgrade");
}
Defensive patterns

Strategy: retry

Validate before calling

// Verify session is valid before syncing
const { data: { session } } = await supabase.auth.getSession();
if (!session?.access_token) {
  await refreshSession();
  return;
}

Try / catch

try {
  await synchronizeUserUpgrade(authMethod);
} catch (e) {
  if (e instanceof Error && (e.message.includes('401') || e.message.includes('403'))) {
    // Session expired — re-authenticate and retry once
    await refreshSession();
    await synchronizeUserUpgrade(authMethod);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: The backend session cookie has expired between the Supabase auth upgrade and this sync call; the backend security context cannot resolve the user from the cookie; the backend throws during the upgrade (e.g., user already upgraded, database error); the backend is down or returns 5xx.

Common situations: Session cookie expired (credentials:'include' sends cookies but they may be stale); backend restarted and lost session state; user's anonymous session in the backend doesn't match the newly authenticated Supabase identity; CORS preflight failure blocking the POST.

Related errors


AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13). Data as JSON: /api/errors/5c81b6b82b757054. Report an issue: GitHub.