{"record":{"id":"73f6559743e2a540","repo":"amruthpillai/reactive-resume","slug":"bad-request-73f655","errorCode":"BAD_REQUEST","errorMessage":"AI provider must be tested and enabled before use.","messagePattern":"AI provider must be tested and enabled before use\\.","errorType":"http","errorClass":"ORPCError","httpStatus":400,"severity":"error","filePath":"packages/api/src/features/ai-providers/service.ts","lineNumber":122,"sourceCode":"\n\t\tconst providers = await db\n\t\t\t.select()\n\t\t\t.from(schema.aiProvider)\n\t\t\t.where(eq(schema.aiProvider.userId, input.userId))\n\t\t\t.orderBy(\n\t\t\t\tdesc(sql<Date>`coalesce(${schema.aiProvider.lastUsedAt}, '1970-01-01T00:00:00.000Z'::timestamptz)`),\n\t\t\t\tasc(schema.aiProvider.createdAt),\n\t\t\t);\n\n\t\treturn providers.map(toResponse);\n\t},\n\n\tgetRunnableById: async (input: { id: string; userId: string }) => {\n\t\tassertCredentialEncryptionConfigured();\n\n\t\tconst provider = await getOwnedProvider(input);\n\t\tif (!provider.enabled || provider.testStatus !== \"success\") {\n\t\t\tthrow new ORPCError(\"BAD_REQUEST\", { message: \"AI provider must be tested and enabled before use.\" });\n\t\t}\n\n\t\treturn {\n\t\t\t...toResponse(provider),\n\t\t\tapiKey: decryptCredential(provider.encryptedApiKey),\n\t\t\tbaseURL: provider.baseUrl ?? \"\",\n\t\t};\n\t},\n\n\tgetDefaultRunnable: async (input: { userId: string }) => {\n\t\tassertCredentialEncryptionConfigured();\n\n\t\tconst [provider] = await db\n\t\t\t.select()\n\t\t\t.from(schema.aiProvider)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(schema.aiProvider.userId, input.userId),","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/amruthpillai/reactive-resume/blob/3a5b12e2a40374a9571988701fcb75c5a1831c42/packages/api/src/features/ai-providers/service.ts#L104-L140","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n// skip test, directly call AI feature\nawait ai.parsePdf({ aiProviderId: 'prov_123', file });\n\n// after\nawait aiProviders.test({ id: 'prov_123' }); // sets testStatus success\nawait aiProviders.update({ id: 'prov_123', enabled: true });\nawait ai.parsePdf({ aiProviderId: 'prov_123', file });","handlingStrategy":"validation","validationCode":"function isRunnableProvider(p) {\n  return !!p && p.enabled === true && p.testStatus === 'success';\n}","typeGuard":"function isRunnableProvider(p) {\n  return !!p && p.enabled === true && p.testStatus === 'success';\n}","tryCatchPattern":"try {\n  await aiFeature({ aiProviderId });\n} catch (e) {\n  if (e.code === 'BAD_REQUEST' && /tested and enabled/i.test(e.message)) {\n    await testProvider({ id: aiProviderId });\n    await enableProvider({ id: aiProviderId });\n    await aiFeature({ aiProviderId });\n  } else throw e;\n}","preventionTips":["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."],"tags":["ai-providers","lifecycle","test","enabled","validation","bad-request"],"backgroundTag":null,"analyzedSha":"3a5b12e2a40374a9571988701fcb75c5a1831c42","analyzedAt":"2026-08-12T22:31:22.666Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}