{"record":{"id":"42ed57408576d5dd","repo":"amruthpillai/reactive-resume","slug":"bad-request-42ed57","errorCode":"BAD_REQUEST","errorMessage":"Invalid AI provider configuration.","messagePattern":"Invalid AI provider configuration\\.","errorType":"http","errorClass":"ORPCError","httpStatus":400,"severity":"error","filePath":"packages/api/src/features/ai-providers/router.ts","lineNumber":15,"sourceCode":"import type { AiProviderResponse } from \"./service\";\nimport { ORPCError } from \"@orpc/client\";\nimport { type } from \"@orpc/server\";\nimport z from \"zod\";\nimport { protectedProcedure } from \"../../context\";\nimport { aiRequestRateLimit } from \"../../middleware/rate-limit\";\nimport { providerInput, updateProviderInput } from \"./inputs\";\nimport { aiProvidersService } from \"./service\";\n\nfunction isInvalidAiBaseUrl(error: unknown) {\n\treturn error instanceof Error && error.message === \"INVALID_AI_BASE_URL\";\n}\n\nfunction throwInvalidProviderConfig(): never {\n\tthrow new ORPCError(\"BAD_REQUEST\", { message: \"Invalid AI provider configuration.\" });\n}\n\nexport const aiProvidersRouter = {\n\tlist: protectedProcedure\n\t\t.route({\n\t\t\tmethod: \"GET\",\n\t\t\tpath: \"/ai-providers\",\n\t\t\ttags: [\"AI Providers\"],\n\t\t\toperationId: \"listAiProviders\",\n\t\t\tsummary: \"List saved AI providers\",\n\t\t\tdescription: \"Lists saved provider/model/API key combinations for the authenticated user. API keys are redacted.\",\n\t\t})\n\t\t.output(type<AiProviderResponse[]>())\n\t\t.errors({\n\t\t\tPRECONDITION_FAILED: { message: \"AI agent workspace is not configured.\", status: 412 },\n\t\t})\n\t\t.handler(({ context }) => aiProvidersService.list({ userId: context.user.id })),\n","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/amruthpillai/reactive-resume/blob/3a5b12e2a40374a9571988701fcb75c5a1831c42/packages/api/src/features/ai-providers/router.ts#L1-L33","documentation":"Generic BAD_REQUEST thrown by the throwInvalidProviderConfig helper in the ai-providers router. It is reached when an underlying service call rejects with an Error whose message is exactly 'INVALID_AI_BASE_URL' (detected by isInvalidAiBaseUrl), meaning the supplied baseURL failed normalization/validation. The helper is shared by the create, update, and test handlers.","triggerScenarios":"POST/PATCH /ai-providers or POST /ai-providers/{id}/test where the baseURL field is malformed (non-URL string, wrong scheme, unreachable host during normalization) causing the service to throw the sentinel 'INVALID_AI_BASE_URL' error, which the router remaps to this message.","commonSituations":"User enters a baseURL without the https:// scheme, with a trailing path that the provider rejects, with a typo, or pointing to a provider-specific endpoint path that is invalid for the chosen provider kind; copy-paste errors from docs that include extra quotes or whitespace.","solutions":["Provide a fully-qualified https:// baseURL with no trailing slash and no /v1 or /chat/completions suffix unless the provider requires it.","Trim and validate the baseURL with new URL() on the client before submit.","Check the provider's documented API base (e.g. https://api.openai.com/v1) and match the selected provider enum.","For local/self-hosted gateways, confirm the host is reachable and serves the expected OpenAI-compatible schema."],"exampleFix":"// before\nbaseURL: 'openai.com/v1'\n\n// after\nbaseURL: 'https://api.openai.com/v1'","handlingStrategy":"validation","validationCode":"function validateBaseUrl(baseURL) {\n  if (!baseURL) return undefined;\n  const u = new URL(baseURL);\n  if (u.protocol !== 'http:' && u.protocol !== 'https:') throw new Error('INVALID_AI_BASE_URL');\n  return u.origin + (u.pathname.replace(/\\/$/, ''));\n}","typeGuard":"function looksLikeValidAiBaseUrl(s) {\n  try { const u = new URL(s); return u.protocol === 'https:' && /^https:\\/\\//.test(s); } catch { return false; }\n}","tryCatchPattern":"try {\n  await createProvider(input);\n} catch (e) {\n  if (e.code === 'BAD_REQUEST' && /Invalid AI provider configuration/i.test(e.message)) {\n    showFieldError('baseURL', 'Enter a full https:// URL for the provider.');\n  } else throw e;\n}","preventionTips":["Validate baseURL with new URL() on the client before submit.","Provide provider-specific baseURL presets to avoid typos.","Strip trailing slashes and path suffixes the provider does not expect."],"tags":["ai-providers","base-url","config","validation","bad-request"],"backgroundTag":null,"analyzedSha":"3a5b12e2a40374a9571988701fcb75c5a1831c42","analyzedAt":"2026-08-12T22:31:22.666Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}