amruthpillai/reactive-resume · warning · Error

Loading AI providers. Please try again in a moment.

Error message

Loading AI providers. Please try again in a moment.

What it means

Thrown synchronously inside the PDF import branch of the resume import dialog when useHasUsableAiProvider()'s isLoading flag is still true. The hook wraps orpc.aiProviders.list (a TanStack Query); isLoading is true before the first response arrives. This guard prevents calling client.ai.parsePdf before the provider list is known, because parsePdf needs a usable provider to exist. The thrown Error propagates to the onSubmit catch block which renders it as a toast.

Source

Thrown at apps/web/src/dialogs/resume/import.tsx:165

			});

			try {
				let data: ResumeData | undefined;

				if (value.type === "json-resume-json") {
					data = parseJSONResume(await value.file.text());
				}

				if (value.type === "reactive-resume-json") {
					data = parseReactiveResumeJSON(await value.file.text());
				}

				if (value.type === "reactive-resume-v4-json") {
					data = parseReactiveResumeV4JSON(await value.file.text());
				}

				if (value.type === "pdf") {
					if (isLoadingAiProviders) throw new Error(t`Loading AI providers. Please try again in a moment.`);
					if (!hasUsableProvider)
						throw new Error(t`This feature requires a connected AI provider. Please set one up in the settings.`);

					const base64 = await fileToBase64(value.file);

					data = await client.ai.parsePdf({
						file: { name: value.file.name, data: base64 },
					});
				}

				if (value.type === "docx") {
					if (isLoadingAiProviders) throw new Error(t`Loading AI providers. Please try again in a moment.`);
					if (!hasUsableProvider)
						throw new Error(t`This feature requires a connected AI provider. Please set one up in the settings.`);

					const base64 = await fileToBase64(value.file);

					const mediaType =

View on GitHub (pinned to 3a5b12e2a4)

Solutions

  1. Wait a moment and retry the import — this is a transient race that resolves once aiProviders.list settles.
  2. To prevent recurrence, also disable the Import button while providers are loading: add isLoadingAiProviders to the submit button's disabled condition (apps/web/src/dialogs/resume/import.tsx:428).
  3. If it persists, check the Network tab for the aiProviders.list request — a failing/CORS-blocked request leaves the query stuck loading; fix the backend/CORS config.
  4. Ensure the React Query client isn't being recreated on each render, which would reset query cache and reload providers every time.

Example fix

// before: submit disabled only when a provider is known-missing, not while loading
<Button type="submit" disabled={!type || !file || isImporting || (aiRequired && !hasUsableProvider)}>

// after: also block submit while AI providers are still loading
<Button
  type="submit"
  disabled={
    !type || !file || isImporting || (aiRequired && (isLoadingAiProviders || !hasUsableProvider))
  }
>
Defensive patterns

Strategy: validation

Validate before calling

const { hasUsableProvider, isLoading } = useHasUsableAiProvider();
function canImportPdf(): boolean {
  return !isLoading && hasUsableProvider;
}
// gate the submit: only call client.ai.parsePdf when canImportPdf() is true

Type guard

function aiProvidersReady(isLoading: boolean, hasUsable: boolean): boolean {
  return !isLoading && hasUsable;
}

Try / catch

// onSubmit catch already toasts the message; to handle gracefully, pre-check:
if (isLoadingAiProviders) {
  toast.info('Loading AI providers, please wait…');
  return;
}

Prevention

When it happens

Trigger: User opens the import dialog, selects a PDF, and clicks Import during the brief window before the aiProviders.list query resolves on mount. Also possible if the query is stale/refetching after a network change and isLoading flips back. The submit button is supposed to be disabled when aiRequired && !hasUsableProvider, but that disable logic does not account for the loading state, so a fast click can slip through.

Common situations: Slow network or cold cache so the provider list query takes longer than the user's click. The query was unmounted/remounted (dialog re-opened) forcing a fresh fetch. React Query default staleTime causing a refetch on focus that briefly sets isLoading/isFetching. Race between the file-read and the provider-list fetch where the file finishes first.

Related errors


AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12). Data as JSON: /api/errors/ad8a9a84cde1d5db. Report an issue: GitHub.