amruthpillai/reactive-resume · error · ORPCError

BAD_REQUEST

BAD_REQUEST

Error message

No AI provider is configured. Add one in Settings → Integrations to use AI features.

What it means

BAD_REQUEST thrown by resolveModel in the applications AI module when aiProvidersService.getDefaultRunnable returns null — i.e. the user has no provider that is both enabled and testStatus 'success'. It is the applications-feature analogue of error 33, with a user-actionable message pointing to Settings → Integrations.

Source

Thrown at packages/api/src/features/applications/ai.ts:40

const LINKEDIN_FETCH_RETRIES = 3;
type ValidatedAddress = { address: string; family: 4 | 6 };

type LinkedInJobPosting = {
	title: string;
	company: string | null;
	location: string | null;
	description: string | null;
	seniority: string | null;
	employmentType: string | null;
	jobFunction: string | null;
	industries: string | null;
};

// Resolve the user's default (tested + enabled) AI provider into a ready model instance.
async function resolveModel(userId: string) {
	const provider = await aiProvidersService.getDefaultRunnable({ userId });
	if (!provider) {
		throw new ORPCError("BAD_REQUEST", {
			message: "No AI provider is configured. Add one in Settings → Integrations to use AI features.",
		});
	}
	return getModel({
		provider: provider.provider,
		model: provider.model,
		apiKey: provider.apiKey,
		...(provider.baseURL ? { baseURL: provider.baseURL } : {}),
	});
}

// generateText + tolerant JSON extraction + Zod validation. Mirrors the resume-analysis pattern
// (the SDK's generateObject isn't wired for every provider here, so we parse defensively).
async function generateJson<T>(model: Awaited<ReturnType<typeof resolveModel>>, prompt: string, schema: z.ZodType<T>) {
	const { text } = await generateText({ model, messages: [{ role: "user", content: prompt }] });
	const fenced = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
	const candidate = fenced?.[1] ?? text;
	const start = candidate.indexOf("{");

View on GitHub (pinned to 3a5b12e2a4)

Solutions

  1. Guide the user to Settings → Integrations to add, test, and enable an AI provider.
  2. On the client, detect the 'no provider' state and show a setup CTA before invoking the feature.
  3. Ensure at least one provider has enabled=true and testStatus='success'.
  4. After adding a provider, run the test and flip enabled on before retrying.

Example fix

// before
await applications.analyzeJobPosting({ url }); // no provider set up

// after
// add + test + enable a provider first, then:
await applications.analyzeJobPosting({ url });
Defensive patterns

Strategy: validation

Validate before calling

async function ensureApplicationsProvider(userId) {
  const provider = await aiProvidersService.getDefaultRunnable({ userId });
  if (!provider) throw new Error('setup required');
  return provider;
}

Type guard

function hasReadyProvider(list) {
  return Array.isArray(list) && list.some(p => p.enabled && p.testStatus === 'success');
}

Try / catch

try {
  await applications.analyzeJobPosting({ url });
} catch (e) {
  if (e.code === 'BAD_REQUEST' && /No AI provider is configured/i.test(e.message)) {
    routeTo('/settings/integrations');
  } else throw e;
}

Prevention

When it happens

Trigger: Invoking any applications AI feature (e.g. job-posting analysis, cover-letter generation) while the user has zero enabled+tested providers. getDefaultRunnable queries for enabled=true AND testStatus='success' and returns null.

Common situations: New user who has not set up an AI provider; the user's provider was disabled or failed its test; all providers reset after a key/model/baseURL change; the user expects a system-wide default that does not exist.

Related errors


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