amruthpillai/reactive-resume · error · ORPCError

BAD_REQUEST

BAD_REQUEST

Error message

AI provider must be tested and enabled before use.

What it means

Thrown by getRunnableById when a specifically-requested provider is either not enabled (enabled !== true) or has not passed its connection test (testStatus !== 'success'). The runnable gate exists so AI features never invoke a provider that the user has not verified; both conditions must be true for the provider to be usable.

Source

Thrown at packages/api/src/features/ai-providers/service.ts:122

		const providers = await db
			.select()
			.from(schema.aiProvider)
			.where(eq(schema.aiProvider.userId, input.userId))
			.orderBy(
				desc(sql<Date>`coalesce(${schema.aiProvider.lastUsedAt}, '1970-01-01T00:00:00.000Z'::timestamptz)`),
				asc(schema.aiProvider.createdAt),
			);

		return providers.map(toResponse);
	},

	getRunnableById: async (input: { id: string; userId: string }) => {
		assertCredentialEncryptionConfigured();

		const provider = await getOwnedProvider(input);
		if (!provider.enabled || provider.testStatus !== "success") {
			throw new ORPCError("BAD_REQUEST", { message: "AI provider must be tested and enabled before use." });
		}

		return {
			...toResponse(provider),
			apiKey: decryptCredential(provider.encryptedApiKey),
			baseURL: provider.baseUrl ?? "",
		};
	},

	getDefaultRunnable: async (input: { userId: string }) => {
		assertCredentialEncryptionConfigured();

		const [provider] = await db
			.select()
			.from(schema.aiProvider)
			.where(
				and(
					eq(schema.aiProvider.userId, input.userId),

View on GitHub (pinned to 3a5b12e2a4)

Solutions

  1. Call POST /ai-providers/{id}/test and ensure it returns success, then PATCH enabled:true.
  2. If the test fails, fix the underlying config (key/baseURL/model) before enabling.
  3. After changing key, model, or baseURL, always re-test — the update flow resets enabled=false and testStatus='untested'.
  4. Provide a UI state that blocks invoking AI features until the selected provider shows testStatus 'success' and enabled true.

Example fix

// before
// skip test, directly call AI feature
await ai.parsePdf({ aiProviderId: 'prov_123', file });

// after
await aiProviders.test({ id: 'prov_123' }); // sets testStatus success
await aiProviders.update({ id: 'prov_123', enabled: true });
await ai.parsePdf({ aiProviderId: 'prov_123', file });
Defensive patterns

Strategy: validation

Validate before calling

function isRunnableProvider(p) {
  return !!p && p.enabled === true && p.testStatus === 'success';
}

Type guard

function isRunnableProvider(p) {
  return !!p && p.enabled === true && p.testStatus === 'success';
}

Try / catch

try {
  await aiFeature({ aiProviderId });
} catch (e) {
  if (e.code === 'BAD_REQUEST' && /tested and enabled/i.test(e.message)) {
    await testProvider({ id: aiProviderId });
    await enableProvider({ id: aiProviderId });
    await aiFeature({ aiProviderId });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling an AI feature (parsePdf, resume analysis, agent run, etc.) with aiProviderId set to a provider row where enabled is false or testStatus is 'untested'/'failed'. The provider exists and is owned by the user but is not in a runnable state.

Common situations: User added a provider but never clicked Test; a previous test failed (testStatus='failed') after a credential or baseURL change; the provider was disabled (enabled=false) via an update that reset testStatus; runtime change (new key/model/baseURL) auto-reset enabled and testStatus per the update logic.

Related errors


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