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
- Call POST /ai-providers/{id}/test and ensure it returns success, then PATCH enabled:true.
- If the test fails, fix the underlying config (key/baseURL/model) before enabling.
- After changing key, model, or baseURL, always re-test — the update flow resets enabled=false and testStatus='untested'.
- 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
- Gate AI feature invocations on isRunnableProvider(selectedProvider).
- After any key/model/baseURL change, re-test before enabling.
- Show provider test/enabled state clearly in the UI.
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.